azalea_block/
block_state.rs1use std::{
2 fmt::{self, Debug},
3 io::{self, Cursor, Write},
4};
5
6use azalea_buf::{AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
7use azalea_registry::builtin::BlockKind;
8
9use crate::BlockTrait;
10
11pub type BlockStateIntegerRepr = u16;
19
20#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)]
28pub struct BlockState {
29 id: BlockStateIntegerRepr,
30}
31
32impl BlockState {
33 pub const AIR: BlockState = BlockState { id: 0 };
38
39 #[inline]
43 pub(crate) const fn new_const(id: BlockStateIntegerRepr) -> Self {
44 assert!(Self::is_valid_state(id));
45 Self { id }
46 }
47
48 #[inline]
53 pub const fn is_valid_state(state_id: BlockStateIntegerRepr) -> bool {
54 state_id <= Self::MAX_STATE
55 }
56
57 #[inline]
61 pub fn is_air(&self) -> bool {
62 self == &Self::AIR
63 }
64
65 #[inline]
70 pub const fn id(&self) -> BlockStateIntegerRepr {
71 self.id
72 }
73}
74
75impl TryFrom<u32> for BlockState {
76 type Error = ();
77
78 fn try_from(state_id: u32) -> Result<Self, Self::Error> {
80 let state_id = state_id as BlockStateIntegerRepr;
81 if Self::is_valid_state(state_id) {
82 Ok(BlockState { id: state_id })
83 } else {
84 Err(())
85 }
86 }
87}
88impl TryFrom<i32> for BlockState {
89 type Error = ();
90
91 fn try_from(state_id: i32) -> Result<Self, Self::Error> {
92 Self::try_from(state_id as u32)
93 }
94}
95
96impl TryFrom<u16> for BlockState {
97 type Error = ();
98
99 fn try_from(state_id: u16) -> Result<Self, Self::Error> {
101 let state_id = state_id as BlockStateIntegerRepr;
102 if Self::is_valid_state(state_id) {
103 Ok(BlockState { id: state_id })
104 } else {
105 Err(())
106 }
107 }
108}
109impl From<BlockState> for u32 {
110 fn from(value: BlockState) -> Self {
112 value.id as u32
113 }
114}
115
116impl AzaleaRead for BlockState {
117 fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
118 let state_id = u32::azalea_read_var(buf)?;
119 Self::try_from(state_id).map_err(|_| BufReadError::UnexpectedEnumVariant {
120 id: state_id as i32,
121 })
122 }
123}
124impl AzaleaWrite for BlockState {
125 fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
126 u32::azalea_write_var(&(self.id as u32), buf)
127 }
128}
129
130impl Debug for BlockState {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 write!(
133 f,
134 "BlockState(id: {}, {:?})",
135 self.id,
136 Box::<dyn BlockTrait>::from(*self)
137 )
138 }
139}
140
141impl From<BlockState> for BlockKind {
142 fn from(value: BlockState) -> Self {
143 Box::<dyn BlockTrait>::from(value).as_registry_block()
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_from_u32() {
153 assert_eq!(
154 BlockState::try_from(0 as BlockStateIntegerRepr).unwrap(),
155 BlockState::AIR
156 );
157
158 assert!(BlockState::try_from(BlockState::MAX_STATE).is_ok());
159 assert!(BlockState::try_from(BlockState::MAX_STATE + 1).is_err());
160 }
161
162 #[test]
163 fn test_from_blockstate() {
164 let block: Box<dyn BlockTrait> = Box::<dyn BlockTrait>::from(BlockState::AIR);
165 assert_eq!(block.id(), "air");
166
167 let block: Box<dyn BlockTrait> =
168 Box::<dyn BlockTrait>::from(BlockState::from(BlockKind::FloweringAzalea));
169 assert_eq!(block.id(), "flowering_azalea");
170 }
171
172 #[test]
173 fn test_debug_blockstate() {
174 let formatted = format!("{:?}", BlockState::from(BlockKind::FloweringAzalea));
175 assert!(formatted.ends_with(", FloweringAzalea)"), "{}", formatted);
176
177 let formatted = format!("{:?}", BlockState::from(BlockKind::BigDripleafStem));
178 assert!(
179 formatted.ends_with(", BigDripleafStem { facing: North, waterlogged: false })"),
180 "{}",
181 formatted
182 );
183 }
184}