1#![doc = include_str!("../README.md")]
2
3mod behavior;
4pub mod block_state;
5pub mod fluid_state;
6mod generated;
7mod range;
8
9use core::fmt::Debug;
10use std::{any::Any, collections::HashMap, str::FromStr};
11
12use azalea_registry::builtin::BlockKind;
13pub use behavior::BlockBehavior;
14pub use block_state::BlockState;
16pub use generated::{blocks, properties};
17pub use range::BlockStates;
18
19pub trait BlockTrait: Debug + Any {
20 fn behavior(&self) -> BlockBehavior;
21 fn id(&self) -> &'static str;
25 fn as_block_state(&self) -> BlockState;
30 fn as_registry_block(&self) -> BlockKind;
35
36 fn property_map(&self) -> HashMap<&'static str, &'static str>;
42 fn get_property(&self, name: &str) -> Option<&'static str>;
49 fn set_property(&mut self, name: &str, new_value: &str) -> Result<(), InvalidPropertyError>;
56}
57
58#[derive(Debug)]
59pub struct InvalidPropertyError;
60
61impl dyn BlockTrait {
62 pub fn downcast_ref<T: BlockTrait>(&self) -> Option<&T> {
63 (self as &dyn Any).downcast_ref::<T>()
64 }
65}
66
67pub trait Property: FromStr {
68 type Value;
69
70 fn try_from_block_state(state: BlockState) -> Option<Self::Value>;
71
72 fn to_static_str(&self) -> &'static str;
74}
75
76#[cfg(test)]
77mod tests {
78 use crate::BlockTrait;
79
80 #[test]
81 pub fn roundtrip_block_state() {
82 let block = crate::blocks::OakTrapdoor {
83 facing: crate::properties::FacingCardinal::East,
84 half: crate::properties::TopBottom::Bottom,
85 open: true,
86 powered: false,
87 waterlogged: false,
88 };
89 let block_state = block.as_block_state();
90 let block_from_state = Box::<dyn BlockTrait>::from(block_state);
91 let block_from_state = *block_from_state
92 .downcast_ref::<crate::blocks::OakTrapdoor>()
93 .unwrap();
94 assert_eq!(block, block_from_state);
95 }
96
97 #[test]
98 pub fn test_property_map() {
99 let block = crate::blocks::OakTrapdoor {
100 facing: crate::properties::FacingCardinal::East,
101 half: crate::properties::TopBottom::Bottom,
102 open: true,
103 powered: false,
104 waterlogged: false,
105 };
106
107 let property_map = block.property_map();
108
109 assert_eq!(property_map.len(), 5);
110 assert_eq!(property_map.get("facing"), Some(&"east"));
111 assert_eq!(property_map.get("half"), Some(&"bottom"));
112 assert_eq!(property_map.get("open"), Some(&"true"));
113 assert_eq!(property_map.get("powered"), Some(&"false"));
114 assert_eq!(property_map.get("waterlogged"), Some(&"false"));
115 }
116
117 #[test]
118 pub fn test_integer_properties() {
119 let sapling_stage_0 = crate::blocks::OakSapling {
121 stage: crate::properties::OakSaplingStage::_0,
122 };
123
124 let sapling_stage_1 = crate::blocks::OakSapling {
125 stage: crate::properties::OakSaplingStage::_1,
126 };
127
128 let properties_0 = sapling_stage_0.property_map();
130 assert_eq!(properties_0.len(), 1);
131 assert_eq!(properties_0.get("stage"), Some(&"0"));
132 assert_eq!(sapling_stage_0.get_property("stage"), Some("0"));
133
134 let properties_1 = sapling_stage_1.property_map();
136 assert_eq!(properties_1.len(), 1);
137 assert_eq!(properties_1.get("stage"), Some(&"1"));
138 assert_eq!(sapling_stage_1.get_property("stage"), Some("1"));
139
140 assert_eq!(sapling_stage_0.get_property("nonexistent"), None);
142 }
143}