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