azalea_block/
lib.rs

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};
11
12pub use behavior::BlockBehavior;
13// re-exported for convenience
14pub use block_state::BlockState;
15pub use generated::{blocks, properties};
16pub use range::BlockStates;
17
18pub trait BlockTrait: Debug + Any {
19    fn behavior(&self) -> BlockBehavior;
20    /// Get the Minecraft string ID for this block.
21    ///
22    /// For example, `stone` or `grass_block`.
23    fn id(&self) -> &'static str;
24    /// Convert the block to a block state.
25    ///
26    /// This is a lossless conversion, as [`BlockState`] also contains state
27    /// data.
28    fn as_block_state(&self) -> BlockState;
29    /// Convert the block to an [`azalea_registry::Block`].
30    ///
31    /// This is a lossy conversion, as [`azalea_registry::Block`] doesn't
32    /// contain any state data.
33    fn as_registry_block(&self) -> azalea_registry::Block;
34
35    /// Returns a map of property names on this block to their values as
36    /// strings.
37    ///
38    /// Consider using [`Self::get_property`] if you only need a single
39    /// property.
40    fn property_map(&self) -> HashMap<&'static str, &'static str>;
41    /// Get a property's value as a string by its name, or `None` if the block
42    /// has no property with that name.
43    ///
44    /// To get all properties, you may use [`Self::property_map`].
45    fn get_property(&self, name: &str) -> Option<&'static str>;
46}
47
48impl dyn BlockTrait {
49    pub fn downcast_ref<T: BlockTrait>(&self) -> Option<&T> {
50        (self as &dyn Any).downcast_ref::<T>()
51    }
52}
53
54pub trait Property {
55    type Value;
56
57    fn try_from_block_state(state: BlockState) -> Option<Self::Value>;
58
59    /// Convert the value of the property to a string, like "x" or "true".
60    fn to_static_str(&self) -> &'static str;
61}
62
63#[cfg(test)]
64mod tests {
65    use crate::BlockTrait;
66
67    #[test]
68    pub fn roundtrip_block_state() {
69        let block = crate::blocks::OakTrapdoor {
70            facing: crate::properties::FacingCardinal::East,
71            half: crate::properties::TopBottom::Bottom,
72            open: true,
73            powered: false,
74            waterlogged: false,
75        };
76        let block_state = block.as_block_state();
77        let block_from_state = Box::<dyn BlockTrait>::from(block_state);
78        let block_from_state = *block_from_state
79            .downcast_ref::<crate::blocks::OakTrapdoor>()
80            .unwrap();
81        assert_eq!(block, block_from_state);
82    }
83
84    #[test]
85    pub fn test_property_map() {
86        let block = crate::blocks::OakTrapdoor {
87            facing: crate::properties::FacingCardinal::East,
88            half: crate::properties::TopBottom::Bottom,
89            open: true,
90            powered: false,
91            waterlogged: false,
92        };
93
94        let property_map = block.property_map();
95
96        assert_eq!(property_map.len(), 5);
97        assert_eq!(property_map.get("facing"), Some(&"east"));
98        assert_eq!(property_map.get("half"), Some(&"bottom"));
99        assert_eq!(property_map.get("open"), Some(&"true"));
100        assert_eq!(property_map.get("powered"), Some(&"false"));
101        assert_eq!(property_map.get("waterlogged"), Some(&"false"));
102    }
103
104    #[test]
105    pub fn test_integer_properties() {
106        // Test with oak sapling that has an integer-like stage property
107        let sapling_stage_0 = crate::blocks::OakSapling {
108            stage: crate::properties::OakSaplingStage::_0,
109        };
110
111        let sapling_stage_1 = crate::blocks::OakSapling {
112            stage: crate::properties::OakSaplingStage::_1,
113        };
114
115        // Test stage 0
116        let properties_0 = sapling_stage_0.property_map();
117        assert_eq!(properties_0.len(), 1);
118        assert_eq!(properties_0.get("stage"), Some(&"0"));
119        assert_eq!(sapling_stage_0.get_property("stage"), Some("0"));
120
121        // Test stage 1
122        let properties_1 = sapling_stage_1.property_map();
123        assert_eq!(properties_1.len(), 1);
124        assert_eq!(properties_1.get("stage"), Some(&"1"));
125        assert_eq!(sapling_stage_1.get_property("stage"), Some("1"));
126
127        // Test non-existent property
128        assert_eq!(sapling_stage_0.get_property("nonexistent"), None);
129    }
130}