azalea_block/
block_state.rs1use std::{
2 fmt::{self, Debug},
3 hint::assert_unchecked,
4 io::{self, Cursor, Write},
5 ops::Deref,
6};
7
8use azalea_buf::{AzBuf, AzBufVar, BufReadError};
9use azalea_registry::builtin::BlockKind;
10
11use crate::BlockTrait;
12
13pub type BlockStateIntegerRepr = u16;
21
22#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)]
30pub struct BlockState {
31 id: BlockStateIntegerRepr,
32}
33
34impl BlockState {
35 pub const AIR: BlockState = BlockState { id: 0 };
41
42 #[inline]
50 pub(crate) const fn new_const(id: BlockStateIntegerRepr) -> Self {
51 assert!(Self::is_valid_state(id));
52 Self { id }
53 }
54
55 #[inline]
57 #[must_use]
58 pub const fn to_trait(self) -> &'static dyn BlockTrait {
59 crate::generated::blocks::blockstate_to_blocktrait(self)
60 }
61
62 #[inline]
67 pub const fn is_valid_state(state_id: BlockStateIntegerRepr) -> bool {
68 state_id <= Self::MAX_STATE
69 }
70
71 #[inline]
75 pub fn is_air(&self) -> bool {
76 *self == Self::AIR
77 }
78
79 #[inline]
84 pub const fn id(&self) -> BlockStateIntegerRepr {
85 unsafe { assert_unchecked(Self::is_valid_state(self.id)) };
90
91 self.id
92 }
93}
94
95impl Deref for BlockState {
96 type Target = dyn BlockTrait;
97
98 #[inline]
99 fn deref(&self) -> &Self::Target {
100 self.to_trait()
101 }
102}
103
104impl TryFrom<u32> for BlockState {
105 type Error = ();
106
107 fn try_from(state_id: u32) -> Result<Self, Self::Error> {
109 let state_id = state_id as BlockStateIntegerRepr;
110 if Self::is_valid_state(state_id) {
111 Ok(BlockState { id: state_id })
112 } else {
113 Err(())
114 }
115 }
116}
117impl TryFrom<i32> for BlockState {
118 type Error = ();
119
120 fn try_from(state_id: i32) -> Result<Self, Self::Error> {
121 Self::try_from(state_id as u32)
122 }
123}
124
125impl TryFrom<u16> for BlockState {
126 type Error = ();
127
128 fn try_from(id: u16) -> Result<Self, Self::Error> {
130 let id = id as BlockStateIntegerRepr;
131 if !Self::is_valid_state(id) {
132 return Err(());
133 }
134 Ok(BlockState { id })
135 }
136}
137impl From<BlockState> for u32 {
138 fn from(value: BlockState) -> Self {
140 value.id as u32
141 }
142}
143
144impl AzBuf for BlockState {
145 fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
146 let state_id = u32::azalea_read_var(buf)?;
147 Self::try_from(state_id).map_err(|_| BufReadError::UnexpectedEnumVariant {
148 id: state_id as i32,
149 })
150 }
151 fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
152 u32::azalea_write_var(&(self.id as u32), buf)
153 }
154}
155
156impl Debug for BlockState {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 write!(f, "BlockState(id: {}, {:?})", self.id, self.to_trait())
159 }
160}
161
162impl From<BlockState> for BlockKind {
163 fn from(block_state: BlockState) -> Self {
164 block_state.as_block_kind()
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test_from_u32() {
174 assert_eq!(
175 BlockState::try_from(0 as BlockStateIntegerRepr).unwrap(),
176 BlockState::AIR
177 );
178
179 assert!(BlockState::try_from(BlockState::MAX_STATE).is_ok());
180 assert!(BlockState::try_from(BlockState::MAX_STATE + 1).is_err());
181 }
182
183 #[test]
184 fn test_from_blockstate() {
185 let block = BlockState::AIR.to_trait();
186 assert_eq!(block.id(), "air");
187
188 let block = BlockState::from(BlockKind::FloweringAzalea).to_trait();
189 assert_eq!(block.id(), "flowering_azalea");
190 }
191
192 #[test]
193 fn test_debug_blockstate() {
194 let formatted = format!("{:?}", BlockState::from(BlockKind::FloweringAzalea));
195 assert!(formatted.ends_with(", FloweringAzalea)"), "{}", formatted);
196
197 let formatted = format!("{:?}", BlockState::from(BlockKind::BigDripleafStem));
198 assert!(
199 formatted.ends_with(", BigDripleafStem { facing: North, waterlogged: false })"),
200 "{}",
201 formatted
202 );
203 }
204}