Skip to content

Commit

Permalink
add unit structs for every bool property
Browse files Browse the repository at this point in the history
  • Loading branch information
mat-1 committed Dec 10, 2023
1 parent c9a18bc commit 3120670
Show file tree
Hide file tree
Showing 4 changed files with 985 additions and 936 deletions.
3 changes: 3 additions & 0 deletions azalea-block/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# cargo expand generated -p azalea-block > azalea-block/out.rs
/out.rs

108 changes: 78 additions & 30 deletions azalea-block/azalea-block-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,36 @@ use std::fmt::Write;
use syn::{
self, braced,
ext::IdentExt,
parenthesized,
parse::{Parse, ParseStream, Result},
parse_macro_input,
punctuated::Punctuated,
Expr, Ident, LitStr, Token,
token, Expr, Ident, LitStr, Token,
};
use utils::{combinations_of, to_pascal_case};

enum PropertyType {
/// `Axis { X, Y, Z }`
Enum {
type_name: Ident,
enum_name: Ident,
variants: Punctuated<Ident, Token![,]>,
},
/// `bool`
Boolean,
/// `Snowy(bool)`
Boolean { struct_name: Ident },
}

/// `"snowy" => bool`
/// `"snowy" => Snowy(bool)`
struct PropertyDefinition {
name: LitStr,
property_type: PropertyType,
}

/// Comma separated PropertyDefinitions (`"snowy" => bool,`)
/// Comma separated PropertyDefinitions (`"snowy" => Snowy(bool),`)
struct PropertyDefinitions {
properties: Vec<PropertyDefinition>,
}

/// `snowy: false` or `axis: properties::Axis::Y`
/// `snowy: Snowy(false)` or `axis: properties::Axis::Y`
#[derive(Debug)]
struct PropertyWithNameAndDefault {
name: Ident,
Expand All @@ -64,21 +65,32 @@ impl Parse for PropertyWithNameAndDefault {
input.parse::<Token![:]>()?;

let first_ident = input.call(Ident::parse_any)?;
let first_ident_string = first_ident.to_string();
let mut property_default = quote! { #first_ident };

let property_type: Ident;
let mut is_enum = false;

if input.parse::<Token![::]>().is_ok() {
// enum
is_enum = true;
property_type = first_ident;
let variant = input.parse::<Ident>()?;
property_default = quote! { properties::#property_default::#variant };
} else if first_ident_string == "true" || first_ident_string == "false" {
property_type = Ident::new("bool", first_ident.span());
} else {
return Err(input.error("Expected a boolean or an enum variant"));
// must be a unit struct if it's not an enum
let content;
let _paren_token: token::Paren = parenthesized!(content in input);
// we use this instead of .parse so it works with rust keywords like true and
// false
let unit_struct_inner = content.call(Ident::parse_any)?;
let unit_struct_inner_string = unit_struct_inner.to_string();

if matches!(unit_struct_inner_string.as_str(), "true" | "false") {
property_type = Ident::new("bool", first_ident.span());
property_default = quote! { #unit_struct_inner };
} else {
return Err(input.error("Expected a boolean or an enum variant"));
}
};

Ok(PropertyWithNameAndDefault {
Expand All @@ -100,20 +112,40 @@ struct MakeBlockStates {

impl Parse for PropertyType {
fn parse(input: ParseStream) -> Result<Self> {
// like `Axis { X, Y, Z }` or `bool`
// like `Axis { X, Y, Z }` or `Waterlogged(bool)`

let keyword = Ident::parse(input)?;
let keyword_string = keyword.to_string();
if keyword_string == "bool" {
Ok(Self::Boolean)
} else {

fn parse_braced(input: ParseStream) -> Result<Punctuated<Ident, Token![,]>> {
let content;
braced!(content in input);
let variants = content.parse_terminated(Ident::parse, Token![,])?;
Ok(variants)
}

fn parse_paren(input: ParseStream) -> Result<Ident> {
let content;
parenthesized!(content in input);
let inner = content.parse::<Ident>()?;
Ok(inner)
}

if let Ok(variants) = parse_braced(input) {
Ok(Self::Enum {
type_name: keyword,
enum_name: keyword,
variants,
})
} else if let Ok(inner) = parse_paren(input) {
assert_eq!(
inner.to_string(),
"bool",
"Currently only bool unit structs are supported"
);
Ok(Self::Boolean {
struct_name: keyword,
})
} else {
Err(input.error("Expected a unit struct or an enum"))
}
}
}
Expand Down Expand Up @@ -238,21 +270,24 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
let mut state_id: u32 = 0;

for property in &input.property_definitions.properties {
let property_type_name: Ident;
let property_struct_name: Ident;
// this is usually the same as property_struct_name except for bool
let property_value_name: Ident;
let mut property_variant_types = Vec::new();

match &property.property_type {
PropertyType::Enum {
type_name,
enum_name,
variants,
} => {
let mut property_enum_variants = quote! {};
let mut property_from_number_variants = quote! {};

property_type_name = type_name.clone();
property_value_name = enum_name.clone();
property_struct_name = enum_name.clone();

property_struct_names_to_names.insert(
property_type_name.to_string(),
property_struct_name.to_string(),
property.name.clone().value(),
);

Expand All @@ -271,19 +306,19 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
// i_lit is used here instead of i because otherwise it says 0size
// in the expansion and that looks uglier
property_from_number_variants.extend(quote! {
#i_lit => #property_type_name::#variant,
#i_lit => #property_struct_name::#variant,
});

property_variant_types.push(variant.to_string());
}

property_enums.extend(quote! {
#[derive(Debug, Clone, Copy)]
pub enum #property_type_name {
pub enum #property_struct_name {
#property_enum_variants
}

impl From<u32> for #property_type_name {
impl From<u32> for #property_struct_name {
fn from(value: u32) -> Self {
match value {
#property_from_number_variants
Expand All @@ -293,15 +328,28 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
}
});
}
PropertyType::Boolean => {
property_type_name = Ident::new("bool", proc_macro2::Span::call_site());
// property_type_name =
// Ident::new(&property.name.value(), proc_macro2::Span::call_site());
PropertyType::Boolean { struct_name } => {
property_value_name = Ident::new("bool", proc_macro2::Span::call_site());
property_struct_name = struct_name.clone();
property_variant_types = vec!["true".to_string(), "false".to_string()];

property_enums.extend(quote! {
#[derive(Debug, Clone, Copy)]
pub struct #property_struct_name(pub bool);

impl From<u32> for #property_struct_name {
fn from(value: u32) -> Self {
match value {
0 => Self(false),
1 => Self(true),
_ => panic!("Invalid property value: {}", value),
}
}
}
});
}
}
properties_map.insert(property_type_name.to_string(), property_variant_types);
// properties_map.insert(property.name.value(), property_variant_types);
properties_map.insert(property_value_name.to_string(), property_variant_types);
}

let mut block_state_enum_variants = quote! {};
Expand Down
Loading

0 comments on commit 3120670

Please sign in to comment.