1mod 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
21type BlockStateIntegerRepr = u16;
23enum PropertyType {
24 Enum {
26 enum_name: Ident,
27 variants: Punctuated<Ident, Token![,]>,
28 },
29 Boolean { struct_name: Ident },
31}
32
33struct PropertyDefinition {
35 name: LitStr,
36 property_type: PropertyType,
37}
38
39struct PropertyDefinitions {
41 properties: Vec<PropertyDefinition>,
42}
43
44#[derive(Debug)]
46struct PropertyWithNameAndDefault {
47 name: String,
49 name_ident: Ident,
52 property_type: Ident,
54 property_value_type: Ident,
55 is_enum: bool,
56 default: proc_macro2::TokenStream,
58}
59
60struct 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 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 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 let content;
93 let _paren_token: token::Paren = parenthesized!(content in input);
94 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub const MAX_STATE: crate::block_state::BlockStateIntegerRepr = #last_state_id;
701
702 pub fn property<P: Property>(self) -> Option<P::Value> {
710 P::try_from_block_state(self)
711 }
712 }
713 };
714
715 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
827fn 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}