mc_core/block/
property.rs1use std::fmt::{Debug, Formatter, Result as FmtResult};
2use std::str::FromStr;
3use std::any::Any;
4
5use crate::pos::{Direction, Axis};
6
7
8pub trait PropertySerializable: 'static + Copy {
10 fn prop_to_string(self) -> String;
11 fn prop_from_string(value: &str) -> Option<Self>;
12}
13
14
15pub trait UntypedProperty: Any + Sync {
17 fn name(&self) -> &'static str;
18 fn len(&self) -> u8;
19 fn prop_to_string(&self, index: u8) -> Option<String>;
20 fn prop_from_string(&self, value: &str) -> Option<u8>;
21}
22
23
24pub trait Property<T: PropertySerializable>: UntypedProperty {
26 fn encode(&self, value: T) -> Option<u8>;
27 fn decode(&self, index: u8) -> Option<T>;
28}
29
30
31impl<T> PropertySerializable for T
32where
33 T: 'static + Copy + ToString + FromStr
34{
35
36 fn prop_to_string(self) -> String {
37 self.to_string()
38 }
39
40 fn prop_from_string(value: &str) -> Option<Self> {
41 Self::from_str(value).ok()
42 }
43
44}
45
46
47pub struct BoolProperty(pub &'static str);
49
50impl UntypedProperty for BoolProperty {
51
52 fn name(&self) -> &'static str { self.0 }
53 fn len(&self) -> u8 { 2 }
54
55 fn prop_to_string(&self, index: u8) -> Option<String> {
56 Some((index != 0).to_string())
57 }
58
59 fn prop_from_string(&self, value: &str) -> Option<u8> {
60 Some(if bool::from_str(value).ok()? { 1 } else { 0 })
61 }
62
63}
64
65impl Property<bool> for BoolProperty {
66 fn encode(&self, value: bool) -> Option<u8> {
67 Some(if value { 1 } else { 0 })
68 }
69 fn decode(&self, index: u8) -> Option<bool> {
70 Some(index != 0)
71 }
72}
73
74
75pub struct IntProperty(pub &'static str, pub u8);
77
78impl UntypedProperty for IntProperty {
79
80 fn name(&self) -> &'static str { self.0 }
81 fn len(&self) -> u8 { self.1 }
82
83 fn prop_to_string(&self, index: u8) -> Option<String> {
84 if index < self.1 { Some(index.to_string()) } else { None }
85 }
86
87 fn prop_from_string(&self, value: &str) -> Option<u8> {
88 let value = u8::from_str(value).ok()?;
89 if value < self.1 { Some(value) } else { None }
90 }
91
92}
93
94impl Property<u8> for IntProperty {
95 fn encode(&self, value: u8) -> Option<u8> {
96 if value < self.1 { Some(value) } else { None }
97 }
98 fn decode(&self, index: u8) -> Option<u8> {
99 if index < self.1 { Some(index) } else { None }
100 }
101}
102
103
104pub struct RangeProperty(pub &'static str, pub u8, pub u8);
106
107impl UntypedProperty for RangeProperty {
108
109 fn name(&self) -> &'static str { self.0 }
110 fn len(&self) -> u8 { self.2 }
111
112 fn prop_to_string(&self, index: u8) -> Option<String> {
113 if index < self.2 { Some((self.1 + index).to_string()) } else { None }
114 }
115
116 fn prop_from_string(&self, value: &str) -> Option<u8> {
117 let value = u8::from_str(value).ok()?.wrapping_sub(self.1);
118 if value < self.2 { Some(value) } else { None }
119 }
120
121}
122
123impl Property<u8> for RangeProperty {
124 fn encode(&self, value: u8) -> Option<u8> {
125 let value = value.wrapping_sub(self.1);
126 if value < self.2 { Some(value) } else { None }
127 }
128 fn decode(&self, index: u8) -> Option<u8> {
129 if index < self.2 { Some(index + self.1) } else { None }
130 }
131}
132
133
134pub struct EnumProperty<T: PropertySerializable + Eq>(pub &'static str, pub &'static [T]);
136
137impl<T> UntypedProperty for EnumProperty<T>
138where
139 T: PropertySerializable + Eq + Sync
140{
141
142 fn name(&self) -> &'static str { self.0 }
143 fn len(&self) -> u8 { self.1.len() as u8 }
144
145 fn prop_to_string(&self, index: u8) -> Option<String> {
146 Some(self.1.get(index as usize)?.prop_to_string())
147 }
148
149 fn prop_from_string(&self, value: &str) -> Option<u8> {
150 let value = T::prop_from_string(value)?;
151 Some(self.1.iter().position(|v| *v == value)? as u8)
152 }
153
154}
155
156impl<T> Property<T> for EnumProperty<T>
157where
158 T: PropertySerializable + Eq + Sync
159{
160 fn encode(&self, value: T) -> Option<u8> {
161 Some(self.1.iter().position(|v| *v == value)? as u8)
162 }
163 fn decode(&self, index: u8) -> Option<T> {
164 Some(*(self.1.get(index as usize)?))
165 }
166}
167
168
169pub struct ArrayEnumProperty<T: PropertySerializable + Eq, const N: usize>(pub &'static str, pub [T; N]);
172
173impl<T, const N: usize> UntypedProperty for ArrayEnumProperty<T, N>
174where
175 T: PropertySerializable + Eq + Sync
176{
177
178 fn name(&self) -> &'static str { self.0 }
179 fn len(&self) -> u8 { N as u8 }
180
181 fn prop_to_string(&self, index: u8) -> Option<String> {
182 Some(self.1.get(index as usize)?.prop_to_string())
183 }
184
185 fn prop_from_string(&self, value: &str) -> Option<u8> {
186 let value = T::prop_from_string(value)?;
187 Some(self.1.iter().position(|v| *v == value)? as u8)
188 }
189
190}
191
192impl<T, const N: usize> Property<T> for ArrayEnumProperty<T, N>
193where
194 T: PropertySerializable + Eq + Sync
195{
196 fn encode(&self, value: T) -> Option<u8> {
197 Some(self.1.iter().position(|v| *v == value)? as u8)
198 }
199 fn decode(&self, index: u8) -> Option<T> {
200 Some(*(self.1.get(index as usize)?))
201 }
202}
203
204
205impl Debug for dyn UntypedProperty {
206 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
207 f.debug_struct("UntypedProperty").field("name", &self.name()).finish()
208 }
209}
210
211
212#[macro_export]
213macro_rules! blocks_properties {
214 ($($v:vis $id:ident: $type_token:tt $params_token:tt;)*) => {
215 $($crate::_blocks_properties_prop!($v $id: $type_token $params_token);)*
216 };
217}
218
219#[macro_export]
220macro_rules! _blocks_properties_prop {
221 ($v:vis $id:ident: int($name:literal, $count:literal)) => {
222 $v static $id: $crate::block::IntProperty = $crate::block::IntProperty($name, $count);
223 };
224 ($v:vis $id:ident: range($name:literal, $offset:literal, $count:literal)) => {
225 $v static $id: $crate::block::RangeProperty = $crate::block::RangeProperty($name, $offset, $count);
226 };
227 ($v:vis $id:ident: bool($name:literal)) => {
228 $v static $id: $crate::block::BoolProperty = $crate::block::BoolProperty($name);
229 };
230 ($v:vis $id:ident: enum($name:literal, $enum_type:ty, [$($value:ident),+])) => {
231 $v static $id: $crate::block::ArrayEnumProperty<$enum_type, {$crate::count!($($value)+)}> = $crate::block::ArrayEnumProperty($name, [$(<$enum_type>::$value),+]);
232 };
233 ($v:vis $id:ident: enum($name:literal, $enum_type:ty, $values_id:ident)) => {
234 $v static $id: $crate::block::EnumProperty<$enum_type> = $crate::block::EnumProperty($name, &$values_id);
235 };
236}
237
238
239#[macro_export]
240macro_rules! impl_enum_serializable {
241 ($enum_id:ident { $($item_id:ident: $item_name:literal),* }) => {
242 impl $crate::block::PropertySerializable for $enum_id {
243 fn prop_to_string(self) -> String {
244 match self {
245 $(Self::$item_id => $item_name),*
246 }.to_string()
247 }
248 fn prop_from_string(value: &str) -> Option<Self> {
249 match value {
250 $($item_name => Some(Self::$item_id),)*
251 _ => None
252 }
253 }
254 }
255 };
256}
257
258
259#[macro_export]
260macro_rules! def_enum_serializable {
261 ($enum_id:ident { $($item_id:ident: $item_name:literal),* }) => {
262
263 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
264 pub enum $enum_id {
265 $($item_id),*
266 }
267
268 $crate::impl_enum_serializable!($enum_id { $($item_id: $item_name),* });
269
270 };
271}
272
273
274impl_enum_serializable!(Direction {
275 East: "east",
276 West: "west",
277 South: "south",
278 North: "north",
279 Up: "up",
280 Down: "down"
281});
282
283impl_enum_serializable!(Axis {
284 X: "x",
285 Y: "y",
286 Z: "z"
287});