mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
start working on az-inv-macros
This commit is contained in:
parent
c4664242e9
commit
2f7bcd54dc
8 changed files with 277 additions and 27 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
@ -258,6 +258,16 @@ name = "azalea-inventory"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"azalea-core",
|
||||
"azalea-inventory-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-inventory-macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1807,9 +1817,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.103"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
|
||||
checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -7,3 +7,4 @@ version = "0.1.0"
|
|||
|
||||
[dependencies]
|
||||
azalea-core = { version = "0.4.0", path = "../azalea-core" }
|
||||
azalea-inventory-macros = { version = "0.1.0", path = "./azalea-inventory-macros" }
|
||||
|
|
14
azalea-inventory/azalea-inventory-macros/Cargo.toml
Normal file
14
azalea-inventory/azalea-inventory-macros/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "azalea-inventory-macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.47"
|
||||
quote = "1.0.21"
|
||||
syn = "1.0.104"
|
41
azalea-inventory/azalea-inventory-macros/src/lib.rs
Normal file
41
azalea-inventory/azalea-inventory-macros/src/lib.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
mod menu_enum;
|
||||
mod menu_impl;
|
||||
mod parse_macro;
|
||||
|
||||
use parse_macro::{DeclareMenus, Field};
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::{self, parse_macro_input, Ident};
|
||||
|
||||
#[proc_macro]
|
||||
pub fn declare_menus(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeclareMenus);
|
||||
|
||||
// implicitly add a `player` field at the end unless an `inventory` field
|
||||
// is present
|
||||
for menu in &mut input.menus {
|
||||
let mut inventory_field_missing = true;
|
||||
for field in menu.fields {
|
||||
if matches!(field.name.to_string().as_str(), "inventory" | "player") {
|
||||
inventory_field_missing = false;
|
||||
}
|
||||
}
|
||||
if inventory_field_missing {
|
||||
menu.fields.push(Field {
|
||||
name: Ident::new("player", Span::call_site()),
|
||||
length: 36,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let menu_enum = menu_enum::generate(input);
|
||||
let menu_impl = menu_impl::generate(input);
|
||||
|
||||
quote! {
|
||||
#menu_enum
|
||||
|
||||
#menu_impl
|
||||
}
|
||||
.into()
|
||||
}
|
51
azalea-inventory/azalea-inventory-macros/src/menu_enum.rs
Normal file
51
azalea-inventory/azalea-inventory-macros/src/menu_enum.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
//! Generate the `enum menu` and nothing else. Implementations are in
|
||||
//! impl_menu.rs
|
||||
|
||||
use crate::parse_macro::{DeclareMenus, Menu};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub fn generate(input: DeclareMenus) -> TokenStream {
|
||||
let mut variants = Vec::new();
|
||||
for menu in input.menus {
|
||||
variants.push(generate_variant_for_menu(menu));
|
||||
}
|
||||
|
||||
quote! {
|
||||
pub enum Menu {
|
||||
#(#variants),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Player {
|
||||
/// craft_result: Slot,
|
||||
/// craft: [Slot; 4],
|
||||
/// armor: [Slot; 4],
|
||||
/// inventory: [Slot; 36],
|
||||
/// offhand: Slot,
|
||||
/// },
|
||||
fn generate_variant_for_menu(menu: Menu) -> TokenStream {
|
||||
let name = menu.name;
|
||||
let mut fields = quote! {};
|
||||
|
||||
for field in menu.fields {
|
||||
let field_name = field.name;
|
||||
|
||||
let field_length = field.length;
|
||||
let field_type = if matches!(field_name.to_string().as_str(), "inventory" | "player") {
|
||||
quote! { std::sync::Arc<[Slot; #field_length ]> }
|
||||
} else if field.length == 1 {
|
||||
quote! { Slot }
|
||||
} else {
|
||||
quote! { [Slot; #field_length ] }
|
||||
};
|
||||
fields.extend(quote! { #field_name: #field_type, })
|
||||
}
|
||||
|
||||
quote! {
|
||||
#name {
|
||||
#fields
|
||||
}
|
||||
}
|
||||
}
|
53
azalea-inventory/azalea-inventory-macros/src/menu_impl.rs
Normal file
53
azalea-inventory/azalea-inventory-macros/src/menu_impl.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
use crate::parse_macro::{DeclareMenus, Menu};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub fn generate(input: DeclareMenus) -> TokenStream {
|
||||
let mut match_variants = quote! {};
|
||||
for menu in input.menus {
|
||||
match_variants.extend(generate_match_variant_for_menu(menu));
|
||||
}
|
||||
|
||||
quote! {
|
||||
impl Menu {
|
||||
/// Get a mutable reference to the [`Slot`] at the given protocol index. If
|
||||
/// you're trying to get an item in a menu normally, you should just
|
||||
/// `match` it and index the `[Slot]` you get
|
||||
pub fn slot_mut(&self, i: usize) -> Option<&Slot> {
|
||||
Some(match self {
|
||||
#match_variants
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Menu::Player {
|
||||
/// craft_result,
|
||||
/// craft,
|
||||
/// armor,
|
||||
/// inventory,
|
||||
/// offhand,
|
||||
/// } => {
|
||||
/// match i {
|
||||
/// 0 => craft_result,
|
||||
/// 1..=4 => craft,
|
||||
/// 5..=8 => armor,
|
||||
/// // ...
|
||||
/// _ => return None,
|
||||
/// }
|
||||
/// } // ...
|
||||
pub fn generate_match_variant_for_menu(menu: Menu) -> TokenStream {
|
||||
let menu_name = menu.name;
|
||||
let menu_field_names = menu.fields.into_iter().map(|f| f.name);
|
||||
|
||||
quote! {
|
||||
Menu::#menu_name {
|
||||
#(#menu_field_names),*
|
||||
} => {
|
||||
match i {
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
73
azalea-inventory/azalea-inventory-macros/src/parse_macro.rs
Normal file
73
azalea-inventory/azalea-inventory-macros/src/parse_macro.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use syn::{
|
||||
self, braced,
|
||||
parse::{Parse, ParseStream, Result},
|
||||
Ident, LitInt, Token,
|
||||
};
|
||||
|
||||
/// An identifier, colon, and number
|
||||
/// `craft_result: 1`
|
||||
pub struct Field {
|
||||
pub name: Ident,
|
||||
pub length: usize,
|
||||
}
|
||||
impl Parse for Field {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let name = input.parse::<Ident>()?;
|
||||
let _ = input.parse::<Token![:]>()?;
|
||||
let length = input.parse::<LitInt>()?.base10_parse()?;
|
||||
Ok(Self { name, length })
|
||||
}
|
||||
}
|
||||
|
||||
/// An identifier and a list of `Field` in curly brackets
|
||||
/// ```rust,ignore
|
||||
/// Player {
|
||||
/// craft_result: 1,
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Menu {
|
||||
pub name: Ident,
|
||||
pub fields: Vec<Field>,
|
||||
}
|
||||
|
||||
impl Parse for Menu {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let name = input.parse::<Ident>()?;
|
||||
|
||||
let content;
|
||||
braced!(content in input);
|
||||
let fields = content
|
||||
.parse_terminated::<Field, Token![,]>(Field::parse)?
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
input.parse::<Token![,]>()?;
|
||||
Ok(Self { name, fields })
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of `Menu`s
|
||||
/// ```rust,ignore
|
||||
/// Player {
|
||||
/// craft_result: 1,
|
||||
/// ...
|
||||
/// },
|
||||
/// ...
|
||||
/// ```
|
||||
pub struct DeclareMenus {
|
||||
pub menus: Vec<Menu>,
|
||||
}
|
||||
impl Parse for DeclareMenus {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let content;
|
||||
braced!(content in input);
|
||||
let menus = content
|
||||
.parse_terminated::<Menu, Token![,]>(Menu::parse)?
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
input.parse::<Token![,]>()?;
|
||||
Ok(Self { menus })
|
||||
}
|
||||
}
|
|
@ -1,13 +1,18 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use azalea_core::Slot;
|
||||
use azalea_inventory_macros::declare_menus;
|
||||
|
||||
// the player inventory part is always the last 36 slots (except in the Player
|
||||
// menu), so we don't have to explicitly specify it
|
||||
|
||||
// if "inventory" is present, then the `player` inventory part doesn't get
|
||||
// implicitly added
|
||||
// Client {
|
||||
// ...
|
||||
// pub menu: Menu,
|
||||
// pub inventory: Arc<[Slot; 36]>
|
||||
// }
|
||||
|
||||
// Generate an `enum Menu` and `impl Menu`.
|
||||
// if the `inventory` field is present, then the `player` field doesn't get
|
||||
// implicitly added
|
||||
declare_menus!({
|
||||
Player {
|
||||
craft_result: 1,
|
||||
|
@ -30,25 +35,27 @@ declare_menus!({
|
|||
}
|
||||
});
|
||||
|
||||
pub enum Menu {
|
||||
Player {
|
||||
craft_result: Slot,
|
||||
craft: [Slot; 4],
|
||||
armor: [Slot; 4],
|
||||
inventory: [Slot; 36],
|
||||
offhand: Slot,
|
||||
},
|
||||
Generic9x1 {
|
||||
contents: Slots<9>,
|
||||
player: Arc<[Slot; 36]>,
|
||||
},
|
||||
Generic9x2 {
|
||||
contents: Slots<18>,
|
||||
player: Arc<[Slot; 36]>,
|
||||
},
|
||||
Generic9x3 {
|
||||
contents: [Slot; 36],
|
||||
player: Arc<[Slot; 36]>,
|
||||
},
|
||||
impl Menu {
|
||||
/// Get a mutable reference to the [`Slot`] at the given protocol index. If
|
||||
/// you're trying to get an item in a menu normally, you should just
|
||||
/// `match` it and index the `[Slot]` you get
|
||||
pub fn slot_mut(&self, i: usize) -> Option<&Slot> {
|
||||
Some(match self {
|
||||
Menu::Player {
|
||||
craft_result,
|
||||
craft,
|
||||
armor,
|
||||
inventory,
|
||||
offhand,
|
||||
} => {
|
||||
match i {
|
||||
0 => craft_result,
|
||||
1..=4 => craft,
|
||||
5..=8 => armor,
|
||||
// ...
|
||||
_ => return None,
|
||||
}
|
||||
} // ...
|
||||
})
|
||||
}
|
||||
}
|
||||
impl Menu {}
|
||||
|
|
Loading…
Add table
Reference in a new issue