1
2
Fork 0
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:
Ubuntu 2022-12-01 02:45:47 +00:00
parent c4664242e9
commit 2f7bcd54dc
8 changed files with 277 additions and 27 deletions

14
Cargo.lock generated
View file

@ -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",

View file

@ -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" }

View 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"

View 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()
}

View 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
}
}
}

View 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,
}
}
}
}

View 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 })
}
}

View file

@ -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 {}