azalea_block_macros/
lib.rs

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