1mod 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
22type BlockStateIntegerRepr = u16;
24enum PropertyType {
25 Enum {
27 enum_name: Ident,
28 variants: Punctuated<Ident, Token![,]>,
29 },
30 Boolean { struct_name: Ident },
32}
33
34struct PropertyDefinition {
36 name: LitStr,
37 property_type: PropertyType,
38}
39
40struct PropertyDefinitions {
42 properties: Vec<PropertyDefinition>,
43}
44
45#[derive(Debug)]
47struct PropertyWithNameAndDefault {
48 name: String,
50 name_ident: Ident,
53 property_type: Ident,
55 property_value_type: Ident,
56 is_enum: bool,
57 default: proc_macro2::TokenStream,
59}
60
61struct 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 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 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 let content;
94 let _paren_token: token::Paren = parenthesized!(content in input);
95 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub const MAX_STATE: crate::block_state::BlockStateIntegerRepr = #last_state_id;
702
703 pub fn property<P: Property>(self) -> Option<P::Value> {
711 P::try_from_block_state(self)
712 }
713 }
714 };
715
716 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
828fn 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}