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