azalea_block_macros/
lib.rs

1//! An internal crate used by `azalea_block`.
2
3mod utils;
4
5use std::{collections::HashMap, fmt::Write};
6
7use proc_macro::TokenStream;
8use proc_macro2::TokenTree;
9use quote::quote;
10use syn::{
11    Expr, Ident, LitStr, Token, braced,
12    ext::IdentExt,
13    parenthesized,
14    parse::{Parse, ParseStream, Result},
15    parse_macro_input,
16    punctuated::Punctuated,
17    token,
18};
19use utils::{combinations_of, to_pascal_case};
20
21// must be the same as the type in `azalea-block/src/lib.rs`
22type BlockStateIntegerRepr = u16;
23enum PropertyType {
24    /// `Axis { X, Y, Z }`
25    Enum {
26        enum_name: Ident,
27        variants: Punctuated<Ident, Token![,]>,
28    },
29    /// `Snowy(bool)`
30    Boolean { struct_name: Ident },
31}
32
33/// `"snowy" => Snowy(bool)`
34struct PropertyDefinition {
35    name: LitStr,
36    property_type: PropertyType,
37}
38
39/// Comma separated PropertyDefinitions (`"snowy" => Snowy(bool),`)
40struct PropertyDefinitions {
41    properties: Vec<PropertyDefinition>,
42}
43
44/// `"snowy": Snowy(false)` or `"axis": properties::Axis::Y`
45#[derive(Debug)]
46struct PropertyWithNameAndDefault {
47    // "snowy" "axis"
48    name: String,
49    /// The property name, potentially modified so it works better as a struct
50    /// field.
51    name_ident: Ident,
52    // Snowy / Axis
53    property_type: Ident,
54    property_value_type: Ident,
55    is_enum: bool,
56    // false / properties::Axis::Y
57    default: proc_macro2::TokenStream,
58}
59
60/// ```ignore
61/// grass_block => BlockBehavior::default(), {
62///   "snowy": false,
63/// },
64/// ```
65struct BlockDefinition {
66    name: Ident,
67    behavior: Expr,
68    properties_and_defaults: Vec<PropertyWithNameAndDefault>,
69}
70impl Parse for PropertyWithNameAndDefault {
71    fn parse(input: ParseStream) -> Result<Self> {
72        // `"snowy": Snowy(false)` or `"axis": properties::Axis::Y`
73        let property_name = input.parse::<LitStr>()?.value();
74        input.parse::<Token![:]>()?;
75
76        let first_ident = input.call(Ident::parse_any)?;
77        let mut property_default = quote! { #first_ident };
78
79        let property_type: Ident;
80        let property_value_type: Ident;
81        let mut is_enum = false;
82
83        if input.parse::<Token![::]>().is_ok() {
84            // enum
85            is_enum = true;
86            property_type = first_ident.clone();
87            property_value_type = first_ident;
88            let variant = input.parse::<Ident>()?;
89            property_default = quote! { properties::#property_default::#variant };
90        } else {
91            // must be a unit struct if it's not an enum
92            let content;
93            let _paren_token: token::Paren = parenthesized!(content in input);
94            // we use this instead of .parse so it works with rust keywords like true and
95            // false
96            let unit_struct_inner = content.call(Ident::parse_any)?;
97            let unit_struct_inner_string = unit_struct_inner.to_string();
98
99            if matches!(unit_struct_inner_string.as_str(), "true" | "false") {
100                property_value_type = Ident::new("bool", first_ident.span());
101                property_type = first_ident;
102                property_default = quote! { #unit_struct_inner };
103            } else {
104                return Err(input.error("Expected a boolean or an enum variant"));
105            }
106        };
107
108        let property_name_ident = name_to_ident(&property_name);
109
110        Ok(PropertyWithNameAndDefault {
111            name: property_name,
112            name_ident: property_name_ident,
113            property_type,
114            property_value_type,
115            is_enum,
116            default: property_default,
117        })
118    }
119}
120
121struct BlockDefinitions {
122    blocks: Vec<BlockDefinition>,
123}
124struct MakeBlockStates {
125    property_definitions: PropertyDefinitions,
126    block_definitions: BlockDefinitions,
127}
128
129impl Parse for PropertyType {
130    fn parse(input: ParseStream) -> Result<Self> {
131        // like `Axis { X, Y, Z }` or `Waterlogged(bool)`
132
133        let keyword = Ident::parse(input)?;
134
135        fn parse_braced(input: ParseStream) -> Result<Punctuated<Ident, Token![,]>> {
136            let content;
137            braced!(content in input);
138            let variants = content.parse_terminated(Ident::parse, Token![,])?;
139            Ok(variants)
140        }
141
142        fn parse_paren(input: ParseStream) -> Result<Ident> {
143            let content;
144            parenthesized!(content in input);
145            let inner = content.parse::<Ident>()?;
146            Ok(inner)
147        }
148
149        if let Ok(variants) = parse_braced(input) {
150            Ok(Self::Enum {
151                enum_name: keyword,
152                variants,
153            })
154        } else if let Ok(inner) = parse_paren(input) {
155            assert_eq!(
156                inner.to_string(),
157                "bool",
158                "Currently only bool unit structs are supported"
159            );
160            Ok(Self::Boolean {
161                struct_name: keyword,
162            })
163        } else {
164            Err(input.error("Expected a unit struct or an enum"))
165        }
166    }
167}
168
169impl Parse for PropertyDefinition {
170    fn parse(input: ParseStream) -> Result<Self> {
171        // "face" => Face {
172        //     Floor,
173        //     Wall,
174        //     Ceiling
175        // },
176
177        // if you're wondering, the reason it's in quotes is because `type` is
178        // a keyword in rust so if we don't put it in quotes it results in a
179        // syntax error
180        let name = input.parse()?;
181        input.parse::<Token![=>]>()?;
182        let property_type = input.parse()?;
183
184        input.parse::<Token![,]>()?;
185        Ok(PropertyDefinition {
186            name,
187            property_type,
188        })
189    }
190}
191
192impl Parse for PropertyDefinitions {
193    fn parse(input: ParseStream) -> Result<Self> {
194        let mut property_definitions = Vec::new();
195        while !input.is_empty() {
196            property_definitions.push(input.parse()?);
197        }
198
199        Ok(PropertyDefinitions {
200            properties: property_definitions,
201        })
202    }
203}
204
205impl Parse for BlockDefinition {
206    fn parse(input: ParseStream) -> Result<Self> {
207        // acacia_button => BlockBehavior::new().strength(0.5, 0.5), {
208        //     "face": Face::Wall,
209        //     "facing": FacingCardinal::North,
210        //     "powered": Powered(false),
211        // },
212        let name = input.parse()?;
213        input.parse::<Token![=>]>()?;
214        let behavior = input.parse()?;
215
216        input.parse::<Token![,]>()?;
217        let content;
218        braced!(content in input);
219
220        let mut properties_and_defaults = Vec::new();
221
222        // read the things comma-separated
223        let property_and_default_punctuated =
224            content.parse_terminated(PropertyWithNameAndDefault::parse, Token![,])?;
225
226        for property_and_default in property_and_default_punctuated {
227            properties_and_defaults.push(property_and_default);
228        }
229
230        Ok(BlockDefinition {
231            name,
232            behavior,
233            properties_and_defaults,
234        })
235    }
236}
237
238impl Parse for BlockDefinitions {
239    fn parse(input: ParseStream) -> Result<Self> {
240        let mut blocks = Vec::new();
241
242        let block_definitions_punctuated =
243            input.parse_terminated(BlockDefinition::parse, Token![,])?;
244        for block_definition in block_definitions_punctuated {
245            blocks.push(block_definition);
246        }
247
248        Ok(BlockDefinitions { blocks })
249    }
250}
251
252impl Parse for MakeBlockStates {
253    fn parse(input: ParseStream) -> Result<Self> {
254        // Properties => { ... } Blocks => { ... }
255        let properties_ident = input.parse::<Ident>()?;
256        assert_eq!(properties_ident.to_string(), "Properties");
257        input.parse::<Token![=>]>()?;
258        let content;
259        braced!(content in input);
260        let properties = content.parse()?;
261
262        input.parse::<Token![,]>()?;
263
264        let blocks_ident = input.parse::<Ident>()?;
265        assert_eq!(blocks_ident.to_string(), "Blocks");
266        input.parse::<Token![=>]>()?;
267        let content;
268        braced!(content in input);
269        let blocks = content.parse()?;
270
271        Ok(MakeBlockStates {
272            property_definitions: properties,
273            block_definitions: blocks,
274        })
275    }
276}
277
278struct PropertyVariantData {
279    pub block_state_ids: Vec<BlockStateIntegerRepr>,
280    pub ident: Ident,
281    pub is_enum: bool,
282}
283
284#[proc_macro]
285pub fn make_block_states(input: TokenStream) -> TokenStream {
286    let input = parse_macro_input!(input as MakeBlockStates);
287
288    let mut property_enums = quote! {};
289    let mut properties_map = HashMap::new();
290    let mut property_struct_names_to_names = HashMap::new();
291
292    let mut state_id: BlockStateIntegerRepr = 0;
293
294    for property in &input.property_definitions.properties {
295        let property_struct_name: Ident;
296        // this is usually the same as property_struct_name except for bool
297        let property_value_name: Ident;
298        let mut property_variant_types = Vec::new();
299
300        match &property.property_type {
301            PropertyType::Enum {
302                enum_name,
303                variants,
304            } => {
305                let mut property_enum_variants = quote! {};
306                let mut property_from_number_variants = quote! {};
307
308                property_value_name = enum_name.clone();
309                property_struct_name = enum_name.clone();
310
311                property_struct_names_to_names.insert(
312                    property_struct_name.to_string(),
313                    property.name.clone().value(),
314                );
315
316                for i in 0..variants.len() {
317                    let variant = &variants[i];
318
319                    let i_lit = syn::Lit::Int(syn::LitInt::new(
320                        &i.to_string(),
321                        proc_macro2::Span::call_site(),
322                    ));
323
324                    property_enum_variants.extend(quote! {
325                        #variant = #i_lit,
326                    });
327
328                    // i_lit is used here instead of i because otherwise it says 0size
329                    // in the expansion and that looks uglier
330                    property_from_number_variants.extend(quote! {
331                        #i_lit => #property_struct_name::#variant,
332                    });
333
334                    property_variant_types.push(variant.to_string());
335                }
336
337                property_enums.extend(quote! {
338                    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
339                    pub enum #property_struct_name {
340                        #property_enum_variants
341                    }
342
343                    impl From<crate::block_state::BlockStateIntegerRepr> for #property_struct_name {
344                        fn from(value: crate::block_state::BlockStateIntegerRepr) -> Self {
345                            match value {
346                                #property_from_number_variants
347                                _ => panic!("Invalid property value: {}", value),
348                            }
349                        }
350                    }
351                });
352            }
353            PropertyType::Boolean { struct_name } => {
354                property_value_name = Ident::new("bool", proc_macro2::Span::call_site());
355                property_struct_name = struct_name.clone();
356                property_variant_types = vec!["true".to_string(), "false".to_string()];
357
358                property_enums.extend(quote! {
359                    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
360                    pub struct #property_struct_name(pub bool);
361
362                    impl From<crate::block_state::BlockStateIntegerRepr> for #property_struct_name {
363                        fn from(value: crate::block_state::BlockStateIntegerRepr) -> Self {
364                            match value {
365                                0 => Self(false),
366                                1 => Self(true),
367                                _ => panic!("Invalid property value: {}", value),
368                            }
369                        }
370                    }
371                });
372            }
373        }
374        properties_map.insert(property_value_name.to_string(), property_variant_types);
375    }
376
377    let mut block_state_enum_variants = quote! {};
378    let mut block_structs = quote! {};
379
380    let mut from_state_to_block_match = quote! {};
381    let mut from_registry_block_to_block_match = quote! {};
382    let mut from_registry_block_to_blockstate_match = quote! {};
383    let mut from_registry_block_to_blockstates_match = quote! {};
384
385    // {
386    //     Waterlogged: [
387    //         [ vec of waterlogged = true state ids ],
388    //         [ vec of waterlogged = false state ids ]
389    //     }
390    // }
391    let mut properties_to_state_ids: HashMap<String, Vec<PropertyVariantData>> = HashMap::new();
392
393    for block in &input.block_definitions.blocks {
394        let block_property_names = &block
395            .properties_and_defaults
396            .iter()
397            .map(|p| p.property_value_type.to_string())
398            .collect::<Vec<_>>();
399        let mut block_properties_vec = Vec::new();
400        for property_name in block_property_names {
401            // if property_name == "stage" {
402            //     panic!("{:?}", block.properties_and_defaults);
403            // }
404            let property_variants = properties_map
405                .get(property_name)
406                .unwrap_or_else(|| panic!("Property '{property_name}' not found"))
407                .clone();
408            block_properties_vec.push(property_variants);
409        }
410
411        let mut properties_with_name: Vec<PropertyWithNameAndDefault> =
412            Vec::with_capacity(block.properties_and_defaults.len());
413        // Used to determine the index of the property so we can optionally add a number
414        // to it
415        let mut previous_names: Vec<String> = Vec::new();
416        for property in &block.properties_and_defaults {
417            let index: Option<usize> = if block
418                .properties_and_defaults
419                .iter()
420                .filter(|p| p.name == property.name)
421                .count()
422                > 1
423            {
424                Some(
425                    previous_names
426                        .iter()
427                        .filter(|&p| p == &property.name)
428                        .count(),
429                )
430            } else {
431                None
432            };
433
434            let mut property_name = property_struct_names_to_names
435                .get(&property.name)
436                .cloned()
437                .unwrap_or_else(|| property.name.clone());
438            previous_names.push(property_name.clone());
439            if let Some(index) = index {
440                // property_name.push_str(&format!("_{}", &index.to_string()));
441                write!(property_name, "_{index}").unwrap();
442            }
443            properties_with_name.push(PropertyWithNameAndDefault {
444                name_ident: name_to_ident(&property_name),
445                name: property_name,
446                property_type: property.property_type.clone(),
447                property_value_type: property.property_value_type.clone(),
448                is_enum: property.is_enum,
449                default: property.default.clone(),
450            });
451        }
452        drop(previous_names);
453
454        //     pub face: properties::Face,
455        //     pub facing: properties::Facing,
456        //     pub powered: properties::Powered,
457        // or
458        //     pub has_bottle_0: HasBottle,
459        //     pub has_bottle_1: HasBottle,
460        //     pub has_bottle_2: HasBottle,
461        let mut block_struct_fields = quote! {};
462        for PropertyWithNameAndDefault {
463            property_value_type,
464            name_ident,
465            is_enum,
466            ..
467        } in &properties_with_name
468        {
469            block_struct_fields.extend(if *is_enum {
470                quote! { pub #name_ident: properties::#property_value_type, }
471            } else {
472                quote! { pub #name_ident: #property_value_type, }
473            });
474        }
475
476        let block_name_pascal_case = Ident::new(
477            &to_pascal_case(&block.name.to_string()),
478            proc_macro2::Span::call_site(),
479        );
480        let block_struct_name = Ident::new(
481            &block_name_pascal_case.to_string(),
482            proc_macro2::Span::call_site(),
483        );
484
485        let mut from_block_to_state_match_inner = quote! {};
486
487        let first_state_id = state_id;
488        let mut default_state_id = None;
489
490        // if there's no properties, then the block is just a single state
491        if block_properties_vec.is_empty() {
492            block_state_enum_variants.extend(quote! {
493                #block_name_pascal_case,
494            });
495            default_state_id = Some(state_id);
496            state_id += 1;
497        }
498        for combination in combinations_of(&block_properties_vec) {
499            let mut is_default = true;
500
501            // 	face: properties::Face::Floor,
502            // 	facing: properties::Facing::North,
503            // 	powered: properties::Powered::True,
504            let mut from_block_to_state_combination_match_inner = quote! {};
505            for i in 0..properties_with_name.len() {
506                let property = &properties_with_name[i];
507                let property_name_ident = &property.name_ident;
508                let property_value_name_ident = &property.property_type;
509                let variant =
510                    Ident::new(&combination[i].to_string(), proc_macro2::Span::call_site());
511
512                // this terrible code just gets the property default as a string
513                let property_default_as_string =
514                    match property.default.clone().into_iter().last().unwrap() {
515                        TokenTree::Ident(ident) => ident.to_string(),
516                        _ => {
517                            panic!()
518                        }
519                    };
520                if property_default_as_string != combination[i] {
521                    is_default = false;
522                }
523
524                let property_variant = if property.is_enum {
525                    quote! {properties::#property_value_name_ident::#variant}
526                } else {
527                    quote! {#variant}
528                };
529
530                from_block_to_state_combination_match_inner.extend(quote! {
531                    #property_name_ident: #property_variant,
532                });
533
534                // add to properties_to_state_ids
535                let property_variants = properties_to_state_ids
536                    .entry(property_value_name_ident.to_string())
537                    .or_default();
538                let property_variant_data =
539                    property_variants.iter_mut().find(|v| v.ident == variant);
540                if let Some(property_variant_data) = property_variant_data {
541                    property_variant_data.block_state_ids.push(state_id);
542                } else {
543                    property_variants.push(PropertyVariantData {
544                        block_state_ids: vec![state_id],
545                        ident: variant,
546                        is_enum: property.is_enum,
547                    });
548                }
549            }
550
551            from_block_to_state_match_inner.extend(quote! {
552                #block_struct_name {
553                    #from_block_to_state_combination_match_inner
554                } => BlockState::new_const(#state_id),
555            });
556
557            if is_default {
558                default_state_id = Some(state_id);
559            }
560
561            state_id += 1;
562        }
563
564        let Some(default_state_id) = default_state_id else {
565            let defaults = properties_with_name
566                .iter()
567                .map(|p| match p.default.clone().into_iter().last().unwrap() {
568                    TokenTree::Ident(i) => i.to_string(),
569                    _ => {
570                        panic!()
571                    }
572                })
573                .collect::<Vec<_>>();
574            panic!(
575                "Couldn't get default state id for {block_name_pascal_case}, combinations={block_properties_vec:?}, defaults={defaults:?}"
576            )
577        };
578
579        // 7035..=7058 => {
580        //     let b = b - 7035;
581        //     &AcaciaButtonBlock {
582        //         powered: properties::Powered::from((b / 1) % 2),
583        //         facing: properties::Facing::from((b / 2) % 4),
584        //         face: properties::Face::from((b / 8) % 3),
585        //     }
586        // }
587        let mut from_state_to_block_inner = quote! {};
588        let mut division: BlockStateIntegerRepr = 1;
589        for i in (0..properties_with_name.len()).rev() {
590            let PropertyWithNameAndDefault {
591                property_type: property_struct_name_ident,
592                name_ident: property_name_ident,
593                property_value_type,
594                ..
595            } = &properties_with_name[i];
596
597            let property_variants = &block_properties_vec[i];
598            let property_variants_count = property_variants.len() as crate::BlockStateIntegerRepr;
599            let conversion_code = {
600                if &property_value_type.to_string() == "bool" {
601                    assert_eq!(property_variants_count, 2);
602                    // this is not a mistake, it starts with true for some reason
603                    quote! {(b / #division) % #property_variants_count == 0}
604                } else {
605                    quote! {properties::#property_struct_name_ident::from((b / #division) % #property_variants_count)}
606                }
607            };
608            from_state_to_block_inner.extend(quote! {
609                #property_name_ident: #conversion_code,
610            });
611
612            division *= property_variants_count;
613        }
614
615        let last_state_id = state_id - 1;
616        from_state_to_block_match.extend(quote! {
617            #first_state_id..=#last_state_id => {
618                let b = b - #first_state_id;
619                Box::new(#block_struct_name {
620                    #from_state_to_block_inner
621                })
622            },
623        });
624        from_registry_block_to_block_match.extend(quote! {
625            azalea_registry::Block::#block_name_pascal_case => Box::new(#block_struct_name::default()),
626        });
627        from_registry_block_to_blockstate_match.extend(quote! {
628            azalea_registry::Block::#block_name_pascal_case => BlockState::new_const(#default_state_id),
629        });
630        from_registry_block_to_blockstates_match.extend(quote! {
631            azalea_registry::Block::#block_name_pascal_case => BlockStates::from(#first_state_id..=#last_state_id),
632        });
633
634        let mut block_default_fields = quote! {};
635        for PropertyWithNameAndDefault {
636            name_ident,
637            default: property_default,
638            ..
639        } in properties_with_name
640        {
641            block_default_fields.extend(quote! { #name_ident: #property_default, });
642        }
643
644        let block_behavior = &block.behavior;
645        let block_id = block.name.to_string();
646
647        let from_block_to_state_match = if block.properties_and_defaults.is_empty() {
648            quote! { BlockState::new_const(#first_state_id) }
649        } else {
650            quote! {
651                match self {
652                    #from_block_to_state_match_inner
653                }
654            }
655        };
656
657        let block_struct = quote! {
658            #[derive(Debug, Copy, Clone)]
659            pub struct #block_struct_name {
660                #block_struct_fields
661            }
662
663            impl BlockTrait for #block_struct_name {
664                fn behavior(&self) -> BlockBehavior {
665                    #block_behavior
666                }
667                fn id(&self) -> &'static str {
668                    #block_id
669                }
670                fn as_block_state(&self) -> BlockState {
671                    #from_block_to_state_match
672                }
673                fn as_registry_block(&self) -> azalea_registry::Block {
674                    azalea_registry::Block::#block_name_pascal_case
675                }
676            }
677
678            impl From<#block_struct_name> for BlockState {
679                fn from(b: #block_struct_name) -> Self {
680                    b.as_block_state()
681                }
682            }
683
684            impl Default for #block_struct_name {
685                fn default() -> Self {
686                    Self {
687                        #block_default_fields
688                    }
689                }
690            }
691        };
692
693        block_structs.extend(block_struct);
694    }
695
696    let last_state_id = state_id - 1;
697    let mut generated = quote! {
698        impl BlockState {
699            /// The highest possible block state ID.
700            pub const MAX_STATE: crate::block_state::BlockStateIntegerRepr = #last_state_id;
701
702            /// Get a property from this block state. Will be `None` if the block can't have the property.
703            ///
704            /// ```
705            /// fn is_waterlogged(block_state: azalea_block::BlockState) -> bool {
706            ///     block_state.property::<azalea_block::properties::Waterlogged>().unwrap_or_default()
707            /// }
708            /// ```
709            pub fn property<P: Property>(self) -> Option<P::Value> {
710                P::try_from_block_state(self)
711            }
712        }
713    };
714
715    // now impl Property for every property
716    // ```
717    // match state_id {
718    //     // this is just an example of how it might look, these state ids are definitely not correct
719    //     0 | 3 | 6 => Some(Self::Axis::X),
720    //     1 | 4 | 7 => Some(Self::Axis::Y),
721    //     2 | 5 | 8 => Some(Self::Axis::Z),
722    //     _ => None,
723    // }
724    // ```
725    let mut property_impls = quote! {};
726    for (property_struct_name, property_values) in properties_to_state_ids {
727        let mut enum_inner_generated = quote! {};
728
729        let mut is_enum_ = false;
730
731        for PropertyVariantData {
732            block_state_ids,
733            ident,
734            is_enum,
735        } in property_values
736        {
737            enum_inner_generated.extend(if is_enum {
738                quote! {
739                    #(#block_state_ids)|* => Some(Self::#ident),
740                }
741            } else {
742                quote! {
743                    #(#block_state_ids)|* => Some(#ident),
744                }
745            });
746            is_enum_ = is_enum;
747        }
748        let is_enum = is_enum_;
749
750        let property_struct_name =
751            Ident::new(&property_struct_name, proc_macro2::Span::call_site());
752
753        let value = if is_enum {
754            quote! { Self }
755        } else {
756            quote! { bool }
757        };
758
759        let property_impl = quote! {
760            impl Property for #property_struct_name {
761                type Value = #value;
762
763                fn try_from_block_state(block_state: BlockState) -> Option<Self::Value> {
764                    match block_state.id() {
765                        #enum_inner_generated
766                        _ => None
767                    }
768                }
769            }
770        };
771        property_impls.extend(property_impl);
772    }
773
774    generated.extend(quote! {
775        pub mod properties {
776            use super::*;
777
778            #property_enums
779
780            #property_impls
781        }
782
783        pub mod blocks {
784            use super::*;
785
786            #block_structs
787
788            impl From<BlockState> for Box<dyn BlockTrait> {
789                fn from(block_state: BlockState) -> Self {
790                    let b = block_state.id();
791                    match b {
792                        #from_state_to_block_match
793                        _ => panic!("Invalid block state: {}", b),
794                    }
795                }
796            }
797            impl From<azalea_registry::Block> for Box<dyn BlockTrait> {
798                fn from(block: azalea_registry::Block) -> Self {
799                    match block {
800                        #from_registry_block_to_block_match
801                        _ => unreachable!("There should always be a block struct for every azalea_registry::Block variant")
802                    }
803                }
804            }
805            impl From<azalea_registry::Block> for BlockState {
806                fn from(block: azalea_registry::Block) -> Self {
807                    match block {
808                        #from_registry_block_to_blockstate_match
809                        _ => unreachable!("There should always be a block state for every azalea_registry::Block variant")
810                    }
811                }
812            }
813            impl From<azalea_registry::Block> for BlockStates {
814                fn from(block: azalea_registry::Block) -> Self {
815                    match block {
816                        #from_registry_block_to_blockstates_match
817                        _ => unreachable!("There should always be a block state for every azalea_registry::Block variant")
818                    }
819                }
820            }
821        }
822    });
823
824    generated.into()
825}
826
827/// Convert a name to a Rust identifier, replacing some Rust keywords with
828/// alternatives (e.g. `type` -> `kind`).
829fn name_to_ident(name: &str) -> Ident {
830    let ident_str = match name {
831        "type" => "kind",
832        _ => name,
833    };
834    Ident::new(ident_str, proc_macro2::Span::call_site())
835}