1use std::{collections::HashMap, iter};
2
3use heck::ToPascalCase;
4use indexmap::IndexMap;
5use itertools::Itertools;
6use proc_macro2::TokenStream;
7use quote::{format_ident, quote};
8
9pub fn build() -> TokenStream {
10 let block_properties: IndexMap<String, IndexMap<String, Vec<String>>> =
11 serde_json::from_slice(include_bytes!("../assets/block_properties.json")).unwrap();
12
13 let mut block_variants = Vec::new();
14 let mut property_structs = Vec::new();
15 let mut state_enums = Vec::new();
16 let mut struct_impls = Vec::new();
17
18 let _block_state_id = 0;
19
20 let mut seen_properties: HashMap<String, Vec<(Vec<String>, String)>> = HashMap::new();
22
23 for (block, properties) in &block_properties {
24 let block_id = block.strip_prefix("block.minecraft").unwrap();
25
26 let variant_name = format_ident!("{}", block_id.to_pascal_case());
27 let struct_name = &variant_name;
28
29 block_variants.push(quote! { #variant_name(#struct_name) });
30
31 let mut struct_fields = Vec::new();
32
33 for (property, states) in properties {
34 let entries = seen_properties.entry(property.clone()).or_default();
35 let assigned_name = if let Some((_, existing_name)) = entries
36 .iter()
37 .find(|(existing_states, _)| existing_states == states)
38 {
39 existing_name.clone()
41 } else {
42 let n = entries.len();
44 let candidate = if n == 0 {
45 property.clone()
46 } else {
47 format!("{}{}", property, variant_name)
48 };
49 entries.push((states.clone(), candidate.clone()));
50
51 let state_enum_name = format_ident!("{}", candidate.to_pascal_case());
52 let state_variants = states.iter().map(|s| {
53 let prefix = if s.chars().next().unwrap().is_ascii_digit() {
54 "Num"
55 } else {
56 ""
57 };
58 format_ident!("{}{}", prefix, s.to_pascal_case())
59 });
60 state_enums.push(quote! {enum #state_enum_name {#(#state_variants),*}});
61
62 candidate
63 };
64
65 let state_enum_name = format_ident!("{}", assigned_name.to_pascal_case());
66
67 let property2 = if property == "type" { "ty" } else { property };
68 let property_ident = format_ident!("{}", property2);
69 struct_fields.push(quote! { #property_ident: #state_enum_name });
70 }
71
72 property_structs.push(quote! { struct #struct_name { #(#struct_fields),*} });
73
74 for _product in properties
76 .iter()
77 .map(|(property, states)| iter::repeat(property).zip(states))
78 .multi_cartesian_product()
79 {
80 let struct_impl = quote! {
81
82 };
84
85 struct_impls.push(struct_impl)
86 }
87 }
88
89 quote! {
90 pub enum Block {
91 #(#block_variants),*
92 }
93
94 #(#property_structs)*
95
96 #(#state_enums)*
97
98 #(#struct_impls)*
99 }
100}
101
102pub trait BlockType {
103 fn block_state_id(&self) -> u16;
104}