1mod property;
4mod utils;
5
6use std::{collections::HashMap, fmt::Write};
7
8use proc_macro::TokenStream;
9use proc_macro2::TokenTree;
10use quote::quote;
11use syn::{
12 Expr, Ident, Token, braced,
13 parse::{Parse, ParseStream, Result},
14 parse_macro_input,
15};
16use utils::{combinations_of, to_pascal_case};
17
18use crate::property::{
19 generate::{
20 generate_properties_code, get_property_value_type, get_property_variant_types,
21 make_property_struct_names_to_names,
22 },
23 parse::{PropertyDefinition, PropertyWithNameAndDefault, parse_property_definitions},
24};
25
26type BlockStateIntegerRepr = u16;
28
29struct BlockDefinition {
35 name: Ident,
36 behavior: Expr,
37 properties_and_defaults: Vec<PropertyWithNameAndDefault>,
38}
39
40struct BlockDefinitions {
41 blocks: Vec<BlockDefinition>,
42}
43
44impl Parse for BlockDefinition {
45 fn parse(input: ParseStream) -> Result<Self> {
46 let name = input.parse()?;
52 input.parse::<Token![=>]>()?;
53 let behavior = input.parse()?;
54
55 input.parse::<Token![,]>()?;
56 let content;
57 braced!(content in input);
58
59 let mut properties_and_defaults = Vec::new();
60
61 let property_and_default_punctuated =
63 content.parse_terminated(PropertyWithNameAndDefault::parse, Token![,])?;
64
65 for property_and_default in property_and_default_punctuated {
66 properties_and_defaults.push(property_and_default);
67 }
68
69 Ok(BlockDefinition {
70 name,
71 behavior,
72 properties_and_defaults,
73 })
74 }
75}
76
77impl Parse for BlockDefinitions {
78 fn parse(input: ParseStream) -> Result<Self> {
79 let mut blocks = Vec::new();
80
81 let block_definitions_punctuated =
82 input.parse_terminated(BlockDefinition::parse, Token![,])?;
83 for block_definition in block_definitions_punctuated {
84 blocks.push(block_definition);
85 }
86
87 Ok(BlockDefinitions { blocks })
88 }
89}
90
91struct MakeBlockStates {
92 pub properties: Vec<PropertyDefinition>,
93 pub blocks: BlockDefinitions,
94}
95impl Parse for MakeBlockStates {
96 fn parse(input: ParseStream) -> Result<Self> {
97 let properties_ident = input.parse::<Ident>()?;
99 assert_eq!(properties_ident.to_string(), "Properties");
100 input.parse::<Token![=>]>()?;
101 let content;
102 braced!(content in input);
103 let properties = parse_property_definitions(&content)?;
104
105 input.parse::<Token![,]>()?;
106
107 let blocks_ident = input.parse::<Ident>()?;
108 assert_eq!(blocks_ident.to_string(), "Blocks");
109 input.parse::<Token![=>]>()?;
110 let content;
111 braced!(content in input);
112 let blocks = content.parse()?;
113
114 Ok(MakeBlockStates { properties, blocks })
115 }
116}
117
118struct PropertyVariantData {
119 pub block_state_ids: Vec<BlockStateIntegerRepr>,
120 pub kind: PropertyKind,
121 pub ident: Ident,
122 pub index: usize,
123}
124
125#[derive(Clone, Copy, Debug, Eq, PartialEq)]
126enum PropertyKind {
127 Enum,
128 Bool,
129}
130
131#[derive(Clone, Debug)]
132struct PropertyVariantMeta {
133 pub ident: Ident,
134 pub index: usize,
135}
136
137#[proc_macro]
138pub fn make_block_states(input: TokenStream) -> TokenStream {
139 let input = parse_macro_input!(input as MakeBlockStates);
140
141 let mut properties_map = HashMap::new();
142 for property in &input.properties {
143 let property_value_type = get_property_value_type(&property.data);
144 let property_variant_types = get_property_variant_types(&property.data);
145 properties_map.insert(property_value_type.to_string(), property_variant_types);
146 }
147
148 let property_struct_names_to_names = make_property_struct_names_to_names(&input.properties);
149
150 let mut block_state_enum_variants = quote! {};
151 let mut block_structs = quote! {};
152
153 let mut from_state_to_block_match = quote! {};
154 let mut from_kind_to_block_match = quote! {};
155 let mut from_kind_to_state_match = quote! {};
156 let mut from_kind_to_states_match = quote! {};
157
158 let mut from_state_to_kind_table = quote! {};
159
160 let mut properties_to_state_ids = HashMap::<String, Vec<PropertyVariantData>>::new();
162
163 let last_block_kind_id = u32::try_from(input.blocks.blocks.len() - 1).unwrap();
164
165 let mut state_id: BlockStateIntegerRepr = 0;
166 for (block_kind_id, block) in input.blocks.blocks.iter().enumerate() {
167 let block_property_names = &block
168 .properties_and_defaults
169 .iter()
170 .map(|p| p.property_value_type.to_string())
171 .collect::<Vec<_>>();
172 let mut block_properties_vec = Vec::new();
173 for property_name in block_property_names {
174 let property_variants = properties_map
175 .get(property_name)
176 .unwrap_or_else(|| panic!("Property '{property_name}' not found"))
177 .clone();
178 block_properties_vec.push(property_variants);
179 }
180
181 let mut properties_with_name: Vec<PropertyWithNameAndDefault> =
182 Vec::with_capacity(block.properties_and_defaults.len());
183 let mut previous_names: Vec<String> = Vec::new();
186 for property in &block.properties_and_defaults {
187 let index: Option<usize> = if block
188 .properties_and_defaults
189 .iter()
190 .filter(|p| p.name == property.name)
191 .count()
192 > 1
193 {
194 Some(
195 previous_names
196 .iter()
197 .filter(|&p| p == &property.name)
198 .count(),
199 )
200 } else {
201 None
202 };
203
204 let mut property_name = property_struct_names_to_names
205 .get(&property.name)
206 .cloned()
207 .unwrap_or_else(|| property.name.clone());
208 previous_names.push(property_name.clone());
209 if let Some(index) = index {
210 write!(property_name, "_{index}").unwrap();
212 }
213 properties_with_name.push(PropertyWithNameAndDefault {
214 name_ident: name_to_ident(&property_name),
215 name: property_name,
216 property_type: property.property_type.clone(),
217 property_value_type: property.property_value_type.clone(),
218 kind: property.kind,
219 default: property.default.clone(),
220 });
221 }
222 drop(previous_names);
223
224 let mut block_struct_fields = quote! {};
232 for PropertyWithNameAndDefault {
233 property_value_type,
234 name_ident,
235 kind,
236 ..
237 } in &properties_with_name
238 {
239 block_struct_fields.extend(match kind {
240 PropertyKind::Enum => {
241 quote! { pub #name_ident: properties::#property_value_type, }
242 }
243 PropertyKind::Bool => {
244 quote! { pub #name_ident: #property_value_type, }
245 }
246 });
247 }
248
249 let block_name_pascal_case = Ident::new(
250 &to_pascal_case(&block.name.to_string()),
251 proc_macro2::Span::call_site(),
252 );
253 let block_struct_name = Ident::new(
254 &block_name_pascal_case.to_string(),
255 proc_macro2::Span::call_site(),
256 );
257
258 let first_state_id = state_id;
259 let mut default_state_id = None;
260
261 if block_properties_vec.is_empty() {
263 block_state_enum_variants.extend(quote! {
264 #block_name_pascal_case,
265 });
266 default_state_id = Some(state_id);
267 state_id += 1;
268 }
269 for combination in combinations_of(&block_properties_vec) {
270 let mut is_default = true;
271
272 let mut from_block_to_state_combination_match_inner = quote! {};
276 for i in 0..properties_with_name.len() {
277 let property = &properties_with_name[i];
278 let property_name_ident = &property.name_ident;
279 let property_value_name_ident = &property.property_type;
280 let variant = &combination[i];
281 let variant_ident = variant.ident.clone();
282
283 let property_default_ident = property
285 .default
286 .clone()
287 .into_iter()
288 .last()
289 .and_then(|tt| match tt {
290 TokenTree::Ident(ident) => Some(ident),
291 _ => None,
292 })
293 .unwrap();
294 if variant.ident != property_default_ident {
295 is_default = false;
296 }
297
298 let property_variant = match property.kind {
299 PropertyKind::Enum => {
300 quote! { properties::#property_value_name_ident::#variant_ident }
301 }
302 PropertyKind::Bool => {
303 quote! { #variant_ident }
304 }
305 };
306
307 from_block_to_state_combination_match_inner.extend(quote! {
308 #property_name_ident: #property_variant,
309 });
310
311 let property_variants = properties_to_state_ids
313 .entry(property_value_name_ident.to_string())
314 .or_default();
315 let property_variant_data = property_variants
316 .iter_mut()
317 .find(|v| v.ident == variant_ident);
318 if let Some(property_variant_data) = property_variant_data {
319 property_variant_data.block_state_ids.push(state_id);
320 } else {
321 property_variants.push(PropertyVariantData {
322 block_state_ids: vec![state_id],
323 ident: variant_ident,
324 index: variant.index,
325 kind: property.kind,
326 });
327 }
328 }
329
330 if is_default {
331 default_state_id = Some(state_id);
332 }
333
334 state_id += 1;
335 }
336
337 let Some(default_state_id) = default_state_id else {
338 let defaults = properties_with_name
339 .iter()
340 .map(|p| match p.default.clone().into_iter().last().unwrap() {
341 TokenTree::Ident(i) => i.to_string(),
342 _ => {
343 panic!()
344 }
345 })
346 .collect::<Vec<_>>();
347 panic!(
348 "Couldn't get default state id for {block_name_pascal_case}, combinations={block_properties_vec:?}, defaults={defaults:?}"
349 )
350 };
351
352 let mut from_state_to_block_inner = quote! {};
361 let mut division: BlockStateIntegerRepr = 1;
362 for i in (0..properties_with_name.len()).rev() {
363 let PropertyWithNameAndDefault {
364 property_type: property_struct_name_ident,
365 name_ident: property_name_ident,
366 property_value_type,
367 ..
368 } = &properties_with_name[i];
369
370 let property_variants = &block_properties_vec[i];
371 let property_variants_count = property_variants.len() as crate::BlockStateIntegerRepr;
372 let conversion_code = {
373 if &property_value_type.to_string() == "bool" {
374 assert_eq!(property_variants_count, 2);
375 quote! {(b / #division) % #property_variants_count == 0}
377 } else {
378 quote! {properties::#property_struct_name_ident::from((b / #division) % #property_variants_count)}
379 }
380 };
381 from_state_to_block_inner.extend(quote! {
382 #property_name_ident: #conversion_code,
383 });
384
385 division *= property_variants_count;
386 }
387
388 let mut as_block_state_inner = quote! { #first_state_id };
389 let mut factor: BlockStateIntegerRepr = 1;
390 for i in (0..properties_with_name.len()).rev() {
391 let PropertyWithNameAndDefault {
392 name_ident: property_name_ident,
393 property_value_type,
394 ..
395 } = &properties_with_name[i];
396
397 let property_variants = &block_properties_vec[i];
398 let property_variants_count = property_variants.len() as crate::BlockStateIntegerRepr;
399 if &property_value_type.to_string() == "bool" {
400 as_block_state_inner.extend(
403 quote! { + (!self.#property_name_ident as BlockStateIntegerRepr) * #factor},
404 );
405 } else {
406 as_block_state_inner.extend(
407 quote! { + (self.#property_name_ident as BlockStateIntegerRepr) * #factor},
408 );
409 };
410
411 factor *= property_variants_count;
412 }
413
414 let last_state_id = state_id - 1;
415 from_state_to_block_match.extend(if first_state_id == last_state_id {
416 quote! {
417 #first_state_id => {
418 Box::new(#block_struct_name { #from_state_to_block_inner })
419 },
420 }
421 } else {
422 quote! {
423 #first_state_id..=#last_state_id => {
424 let b = b - #first_state_id;
425 Box::new(#block_struct_name { #from_state_to_block_inner })
426 },
427 }
428 });
429
430 from_kind_to_block_match.extend(quote! {
431 BlockKind::#block_name_pascal_case => Box::new(#block_struct_name::default()),
432 });
433 from_kind_to_state_match.extend(quote! {
434 BlockKind::#block_name_pascal_case => BlockState::new_const(#default_state_id),
435 });
436 from_kind_to_states_match.extend(quote! {
437 BlockKind::#block_name_pascal_case => BlockStates::from(#first_state_id..=#last_state_id),
438 });
439 for _ in first_state_id..=last_state_id {
440 let block_kind_id = block_kind_id as u32;
441 from_state_to_kind_table.extend(quote! { #block_kind_id, });
442 }
443
444 let mut property_map_inner = quote! {};
445 let mut get_property_match_inner = quote! {};
446 let mut set_property_match_inner = quote! {};
447
448 for PropertyWithNameAndDefault {
449 name,
450 name_ident,
451 kind,
452 ..
453 } in &properties_with_name
454 {
455 let variant_name_tokens = match kind {
456 PropertyKind::Enum => quote! { self.#name_ident.to_static_str() },
457 PropertyKind::Bool => quote! { if self.#name_ident { "true" } else { "false" } },
458 };
459 property_map_inner.extend(quote! {
460 map.insert(#name, #variant_name_tokens);
461 });
462 get_property_match_inner.extend(quote! {
463 #name => Some(#variant_name_tokens),
464 });
465
466 set_property_match_inner.extend(match kind {
467 PropertyKind::Enum => quote! { #name => self.#name_ident = new_value.parse()?, },
468 PropertyKind::Bool => {
469 quote! { #name => self.#name_ident = new_value.parse::<bool>().map_err(|_| InvalidPropertyError)?, }
470 }
471 });
472 }
473 let set_property = if set_property_match_inner.is_empty() {
474 quote! {
475 Err(InvalidPropertyError)
476 }
477 } else {
478 quote! {
479 match name {
480 #set_property_match_inner
481 _ => return Err(InvalidPropertyError),
482 }
483 Ok(())
484 }
485 };
486
487 let mut block_default_fields = quote! {};
488 for PropertyWithNameAndDefault {
489 name_ident,
490 default: property_default,
491 ..
492 } in properties_with_name
493 {
494 block_default_fields.extend(quote! { #name_ident: #property_default, });
495 }
496
497 let block_behavior = &block.behavior;
498 let block_id = block.name.to_string();
499
500 let as_block_state = quote! { BlockState::new_const(#as_block_state_inner) };
501
502 let mut block_struct = quote! {
503 #[derive(Clone, Copy, Debug, PartialEq)]
504 pub struct #block_struct_name
505 };
506 if block_struct_fields.is_empty() {
507 block_struct.extend(quote! {;});
508 } else {
509 block_struct.extend(quote! { { #block_struct_fields } });
510 }
511
512 block_struct.extend(quote! {
513 impl BlockTrait for #block_struct_name {
514 fn behavior(&self) -> BlockBehavior {
515 #block_behavior
516 }
517 fn id(&self) -> &'static str {
518 #block_id
519 }
520 fn as_block_state(&self) -> BlockState {
521 #as_block_state
522 }
523 fn as_block_kind(&self) -> BlockKind {
524 BlockKind::#block_name_pascal_case
525 }
526
527 fn property_map(&self) -> std::collections::HashMap<&'static str, &'static str> {
528 let mut map = std::collections::HashMap::new();
529 #property_map_inner
530 map
531 }
532 fn get_property(&self, name: &str) -> Option<&'static str> {
533 match name {
534 #get_property_match_inner
535 _ => None,
536 }
537 }
538 fn set_property(&mut self, name: &str, new_value: &str) -> Result<(), InvalidPropertyError> {
539 #set_property
540 }
541 }
542
543 impl From<#block_struct_name> for BlockState {
544 fn from(b: #block_struct_name) -> Self {
545 b.as_block_state()
546 }
547 }
548
549 impl Default for #block_struct_name {
550 fn default() -> Self {
551 Self {
552 #block_default_fields
553 }
554 }
555 }
556 });
557
558 block_structs.extend(block_struct);
559 }
560
561 let last_state_id = state_id - 1;
562 let mut generated = quote! {
563 impl BlockState {
564 pub const MAX_STATE: BlockStateIntegerRepr = #last_state_id;
566
567 pub fn property<P: Property>(self) -> Option<P::Value> {
575 P::try_from_block_state(self)
576 }
577
578 pub fn as_block_kind(self) -> BlockKind {
579 static TABLE: &[u32] = &[
580 #from_state_to_kind_table
581 ];
582 const _: () = assert!(BlockKind::is_valid_id(#last_block_kind_id));
583
584 unsafe { BlockKind::from_u32(TABLE[self.id() as usize]).unwrap_unchecked() }
587 }
588 }
589 };
590
591 let properties_code =
592 generate_properties_code(&input.properties, &properties_to_state_ids, last_state_id);
593
594 generated.extend(quote! {
595 pub mod properties {
596 use super::*;
597
598 #properties_code
599 }
600
601 pub mod blocks {
602 use super::*;
603 use azalea_registry::builtin::BlockKind;
604
605 #block_structs
606
607 impl From<BlockState> for Box<dyn BlockTrait> {
608 fn from(block_state: BlockState) -> Self {
609 let b = block_state.id();
610 match b {
611 #from_state_to_block_match
612 _ => panic!("Invalid block state: {}", b),
613 }
614 }
615 }
616 impl From<BlockKind> for Box<dyn BlockTrait> {
617 fn from(block: BlockKind) -> Self {
618 match block {
619 #from_kind_to_block_match
620 _ => unreachable!("There should always be a block struct for every BlockKind variant")
621 }
622 }
623 }
624 impl From<BlockKind> for BlockState {
625 fn from(block: BlockKind) -> Self {
626 match block {
627 #from_kind_to_state_match
628 _ => unreachable!("There should always be a block state for every BlockKind variant")
629 }
630 }
631 }
632 impl From<BlockKind> for BlockStates {
633 fn from(block: BlockKind) -> Self {
634 match block {
635 #from_kind_to_states_match
636 _ => unreachable!("There should always be a block state for every BlockKind variant")
637 }
638 }
639 }
640 }
641 });
642
643 generated.into()
644}
645
646fn name_to_ident(name: &str) -> Ident {
649 let ident_str = match name {
650 "type" => "kind",
651 _ => name,
652 };
653 Ident::new(ident_str, proc_macro2::Span::call_site())
654}