irox_types/
primitive.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5extern crate alloc;
6use alloc::boxed::Box;
7use alloc::string::{String, ToString};
8use core::char::ParseCharError;
9use core::cmp::Ordering;
10use core::fmt::{Display, Formatter};
11use core::hash::{Hash, Hasher};
12use core::num::{ParseFloatError, ParseIntError};
13use core::str::{FromStr, ParseBoolError};
14use irox_enums::{EnumIterItem, EnumName, EnumTryFromStr};
15
16///
17/// A set of possible primitives
18#[allow(non_camel_case_types)]
19#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, EnumName, EnumIterItem, EnumTryFromStr)]
20#[non_exhaustive]
21#[repr(u8)]
22pub enum Primitives {
23    u8,
24    i8,
25    u16,
26    i16,
27    u32,
28    i32,
29    f32,
30    u64,
31    i64,
32    f64,
33    u128,
34    i128,
35
36    bool,
37    char,
38
39    null,
40}
41
42impl TryFrom<u8> for Primitives {
43    type Error = ();
44
45    fn try_from(value: u8) -> Result<Self, Self::Error> {
46        for v in Primitives::iter_items() {
47            if value == v as u8 {
48                return Ok(v);
49            }
50        }
51        Err(())
52    }
53}
54
55pub enum PrimitiveParseError {
56    IntError(ParseIntError),
57    FloatError(ParseFloatError),
58    BoolError(ParseBoolError),
59    CharError(ParseCharError),
60    StringWasNotNullError,
61}
62
63impl From<ParseIntError> for PrimitiveParseError {
64    fn from(value: ParseIntError) -> Self {
65        PrimitiveParseError::IntError(value)
66    }
67}
68
69impl From<ParseFloatError> for PrimitiveParseError {
70    fn from(value: ParseFloatError) -> Self {
71        PrimitiveParseError::FloatError(value)
72    }
73}
74
75impl From<ParseBoolError> for PrimitiveParseError {
76    fn from(value: ParseBoolError) -> Self {
77        PrimitiveParseError::BoolError(value)
78    }
79}
80
81impl From<ParseCharError> for PrimitiveParseError {
82    fn from(value: ParseCharError) -> Self {
83        PrimitiveParseError::CharError(value)
84    }
85}
86
87impl Primitives {
88    /// Returns the number of bytes required to encode/decode this value.
89    #[must_use]
90    #[allow(clippy::match_same_arms)]
91    pub fn bytes_length(&self) -> usize {
92        match self {
93            Primitives::u8 => 1,
94            Primitives::i8 => 1,
95            Primitives::u16 => 2,
96            Primitives::i16 => 2,
97            Primitives::u32 => 4,
98            Primitives::i32 => 4,
99            Primitives::f32 => 4,
100            Primitives::u64 => 8,
101            Primitives::i64 => 8,
102            Primitives::f64 => 8,
103            Primitives::u128 => 16,
104            Primitives::i128 => 16,
105            Primitives::bool => 1,
106            Primitives::char => 4,
107            Primitives::null => 0,
108        }
109    }
110
111    pub fn try_value_from_str(&self, val: &str) -> Result<PrimitiveValue, PrimitiveParseError> {
112        match self {
113            Primitives::u8 => Ok(PrimitiveValue::u8(u8::from_str(val)?)),
114            Primitives::i8 => Ok(PrimitiveValue::i8(i8::from_str(val)?)),
115            Primitives::u16 => Ok(PrimitiveValue::u16(u16::from_str(val)?)),
116            Primitives::i16 => Ok(PrimitiveValue::i16(i16::from_str(val)?)),
117            Primitives::u32 => Ok(PrimitiveValue::u32(u32::from_str(val)?)),
118            Primitives::i32 => Ok(PrimitiveValue::i32(i32::from_str(val)?)),
119            Primitives::f32 => Ok(PrimitiveValue::f32(f32::from_str(val)?)),
120            Primitives::u64 => Ok(PrimitiveValue::u64(u64::from_str(val)?)),
121            Primitives::i64 => Ok(PrimitiveValue::i64(i64::from_str(val)?)),
122            Primitives::f64 => Ok(PrimitiveValue::f64(f64::from_str(val)?)),
123            Primitives::u128 => Ok(PrimitiveValue::u128(u128::from_str(val)?)),
124            Primitives::i128 => Ok(PrimitiveValue::i128(i128::from_str(val)?)),
125            Primitives::bool => Ok(PrimitiveValue::bool(bool::from_str(val)?)),
126            Primitives::char => Ok(PrimitiveValue::char(char::from_str(val)?)),
127            Primitives::null => {
128                if "null".eq_ignore_ascii_case(val) {
129                    Ok(PrimitiveValue::null)
130                } else {
131                    Err(PrimitiveParseError::StringWasNotNullError)
132                }
133            }
134        }
135    }
136
137    #[cfg(feature = "bits")]
138    pub fn read_be_from<T: irox_bits::Bits>(
139        &self,
140        src: &mut T,
141    ) -> Result<PrimitiveValue, irox_bits::BitsError> {
142        match self {
143            Primitives::u8 => Ok(PrimitiveValue::u8(src.read_u8()?)),
144            Primitives::i8 => Ok(PrimitiveValue::i8(src.read_i8()?)),
145            Primitives::u16 => Ok(PrimitiveValue::u16(src.read_be_u16()?)),
146            Primitives::i16 => Ok(PrimitiveValue::i16(src.read_be_i16()?)),
147            Primitives::u32 => Ok(PrimitiveValue::u32(src.read_be_u32()?)),
148            Primitives::i32 => Ok(PrimitiveValue::i32(src.read_be_i32()?)),
149            Primitives::f32 => Ok(PrimitiveValue::f32(src.read_be_f32()?)),
150            Primitives::u64 => Ok(PrimitiveValue::u64(src.read_be_u64()?)),
151            Primitives::i64 => Ok(PrimitiveValue::i64(src.read_be_i64()?)),
152            Primitives::f64 => Ok(PrimitiveValue::f64(src.read_be_f64()?)),
153            Primitives::u128 => Ok(PrimitiveValue::u128(src.read_be_u128()?)),
154            Primitives::i128 => Ok(PrimitiveValue::i128(src.read_be_i128()?)),
155            Primitives::bool => Ok(PrimitiveValue::bool(src.read_bool()?)),
156            Primitives::char => Ok(PrimitiveValue::char(src.read_be_utf8_char()?)),
157            Primitives::null => Ok(PrimitiveValue::null),
158        }
159    }
160}
161
162///
163/// A shuttle struct to pass around a primitive type and an associated value of the same type
164///
165#[allow(non_camel_case_types)]
166#[derive(Debug, Copy, Clone, PartialEq, EnumName)]
167#[non_exhaustive]
168pub enum PrimitiveValue {
169    u8(u8),
170    i8(i8),
171    u16(u16),
172    i16(i16),
173    u32(u32),
174    i32(i32),
175    f32(f32),
176    u64(u64),
177    i64(i64),
178    f64(f64),
179    u128(u128),
180    i128(i128),
181    bool(bool),
182    char(char),
183
184    null,
185}
186impl Hash for PrimitiveValue {
187    fn hash<H: Hasher>(&self, state: &mut H) {
188        self.primitive().hash(state);
189        match self {
190            PrimitiveValue::u8(v) => v.hash(state),
191            PrimitiveValue::i8(v) => v.hash(state),
192            PrimitiveValue::u16(v) => v.hash(state),
193            PrimitiveValue::i16(v) => v.hash(state),
194            PrimitiveValue::u32(v) => v.hash(state),
195            PrimitiveValue::i32(v) => v.hash(state),
196            PrimitiveValue::f32(v) => v.to_bits().hash(state),
197            PrimitiveValue::u64(v) => v.hash(state),
198            PrimitiveValue::i64(v) => v.hash(state),
199            PrimitiveValue::f64(v) => v.to_bits().hash(state),
200            PrimitiveValue::u128(v) => v.hash(state),
201            PrimitiveValue::i128(v) => v.hash(state),
202            PrimitiveValue::bool(v) => v.hash(state),
203            PrimitiveValue::char(v) => v.hash(state),
204            PrimitiveValue::null => {}
205        }
206    }
207}
208
209impl PrimitiveValue {
210    /// Returns the type of this primitive
211    #[must_use]
212    pub const fn primitive(&self) -> Primitives {
213        match self {
214            PrimitiveValue::u8(_) => Primitives::u8,
215            PrimitiveValue::i8(_) => Primitives::i8,
216            PrimitiveValue::u16(_) => Primitives::u16,
217            PrimitiveValue::i16(_) => Primitives::i16,
218            PrimitiveValue::u32(_) => Primitives::u32,
219            PrimitiveValue::i32(_) => Primitives::i32,
220            PrimitiveValue::f32(_) => Primitives::f32,
221            PrimitiveValue::u64(_) => Primitives::u64,
222            PrimitiveValue::i64(_) => Primitives::i64,
223            PrimitiveValue::f64(_) => Primitives::f64,
224            PrimitiveValue::u128(_) => Primitives::u128,
225            PrimitiveValue::i128(_) => Primitives::i128,
226            PrimitiveValue::bool(_) => Primitives::bool,
227            PrimitiveValue::char(_) => Primitives::char,
228            PrimitiveValue::null => Primitives::null,
229        }
230    }
231
232    pub fn as_be_bytes(&self) -> Box<[u8]> {
233        match self {
234            PrimitiveValue::u8(v) => Box::new([*v]),
235            PrimitiveValue::i8(v) => Box::new(v.to_be_bytes()),
236            PrimitiveValue::u16(v) => Box::new(v.to_be_bytes()),
237            PrimitiveValue::i16(v) => Box::new(v.to_be_bytes()),
238            PrimitiveValue::u32(v) => Box::new(v.to_be_bytes()),
239            PrimitiveValue::i32(v) => Box::new(v.to_be_bytes()),
240            PrimitiveValue::f32(v) => Box::new(v.to_be_bytes()),
241            PrimitiveValue::u64(v) => Box::new(v.to_be_bytes()),
242            PrimitiveValue::i64(v) => Box::new(v.to_be_bytes()),
243            PrimitiveValue::f64(v) => Box::new(v.to_be_bytes()),
244            PrimitiveValue::u128(v) => Box::new(v.to_be_bytes()),
245            PrimitiveValue::i128(v) => Box::new(v.to_be_bytes()),
246            PrimitiveValue::bool(v) => {
247                if *v {
248                    Box::new([1])
249                } else {
250                    Box::new([0])
251                }
252            }
253            PrimitiveValue::char(v) => {
254                let mut buf = [0u8; 4];
255                v.encode_utf8(&mut buf);
256                Box::new(buf)
257            }
258            PrimitiveValue::null => Box::new([]),
259        }
260    }
261
262    #[cfg(feature = "bits")]
263    pub fn write_be_to<T: irox_bits::MutBits>(
264        &self,
265        out: &mut T,
266    ) -> Result<(), irox_bits::BitsError> {
267        match self {
268            PrimitiveValue::u8(v) => out.write_u8(*v),
269            PrimitiveValue::i8(v) => out.write_i8(*v),
270            PrimitiveValue::u16(v) => out.write_be_u16(*v),
271            PrimitiveValue::i16(v) => out.write_be_i16(*v),
272            PrimitiveValue::u32(v) => out.write_be_u32(*v),
273            PrimitiveValue::i32(v) => out.write_be_i32(*v),
274            PrimitiveValue::f32(v) => out.write_be_f32(*v),
275            PrimitiveValue::u64(v) => out.write_be_u64(*v),
276            PrimitiveValue::i64(v) => out.write_be_i64(*v),
277            PrimitiveValue::f64(v) => out.write_be_f64(*v),
278            PrimitiveValue::u128(v) => out.write_be_u128(*v),
279            PrimitiveValue::i128(v) => out.write_be_i128(*v),
280            PrimitiveValue::bool(v) => out.write_bool(*v),
281            PrimitiveValue::char(v) => {
282                out.write_be_utf8_char(*v)?;
283                Ok(())
284            }
285            PrimitiveValue::null => Ok(()),
286        }
287    }
288}
289impl Display for PrimitiveValue {
290    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
291        match self {
292            PrimitiveValue::u8(v) => write!(f, "{v}"),
293            PrimitiveValue::i8(v) => write!(f, "{v}"),
294            PrimitiveValue::u16(v) => write!(f, "{v}"),
295            PrimitiveValue::i16(v) => write!(f, "{v}"),
296            PrimitiveValue::u32(v) => write!(f, "{v}"),
297            PrimitiveValue::i32(v) => write!(f, "{v}"),
298            PrimitiveValue::f32(v) => write!(f, "{v}"),
299            PrimitiveValue::u64(v) => write!(f, "{v}"),
300            PrimitiveValue::i64(v) => write!(f, "{v}"),
301            PrimitiveValue::f64(v) => write!(f, "{v}"),
302            PrimitiveValue::u128(v) => write!(f, "{v}"),
303            PrimitiveValue::i128(v) => write!(f, "{v}"),
304            PrimitiveValue::bool(v) => write!(f, "{v}"),
305            PrimitiveValue::char(v) => write!(f, "{v}"),
306
307            PrimitiveValue::null => write!(f, "null"),
308        }
309    }
310}
311macro_rules! impl_into_primivevalue {
312    ($ty:ty, $pv:tt) => {
313        impl From<$ty> for PrimitiveValue {
314            fn from(value: $ty) -> Self {
315                PrimitiveValue::$pv(value)
316            }
317        }
318    };
319}
320impl_into_primivevalue!(u8, u8);
321impl_into_primivevalue!(i8, i8);
322impl_into_primivevalue!(u16, u16);
323impl_into_primivevalue!(i16, i16);
324impl_into_primivevalue!(u32, u32);
325impl_into_primivevalue!(i32, i32);
326impl_into_primivevalue!(f32, f32);
327impl_into_primivevalue!(u64, u64);
328impl_into_primivevalue!(i64, i64);
329impl_into_primivevalue!(f64, f64);
330impl_into_primivevalue!(u128, u128);
331impl_into_primivevalue!(i128, i128);
332impl_into_primivevalue!(bool, bool);
333impl_into_primivevalue!(char, char);
334impl From<()> for PrimitiveValue {
335    fn from((): ()) -> Self {
336        PrimitiveValue::null
337    }
338}
339
340///
341/// A struct to "Name" a primitive - like a Field with an associated type
342///
343#[derive(Debug, Clone, Eq, PartialEq, Hash)]
344pub struct NamedPrimitive {
345    name: String,
346    primitive: Primitives,
347}
348impl Ord for NamedPrimitive {
349    fn cmp(&self, other: &Self) -> Ordering {
350        self.name.cmp(&other.name)
351    }
352}
353impl PartialOrd for NamedPrimitive {
354    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
355        Some(self.cmp(other))
356    }
357}
358
359impl NamedPrimitive {
360    #[must_use]
361    pub fn new(name: String, primitive: Primitives) -> NamedPrimitive {
362        NamedPrimitive { name, primitive }
363    }
364
365    /// Returns the name of the field
366    #[must_use]
367    pub fn name(&self) -> &String {
368        &self.name
369    }
370
371    /// Returns the type of the field
372    #[must_use]
373    pub fn primitive(&self) -> Primitives {
374        self.primitive
375    }
376}
377
378///
379/// A struct to "Name" a primitive with an associated value, like a Field with a value
380///
381#[derive(Debug, Clone, PartialEq)]
382pub struct NamedPrimitiveValue {
383    pub(crate) name: String,
384    pub(crate) value: PrimitiveValue,
385}
386
387impl NamedPrimitiveValue {
388    #[must_use]
389    pub fn new(name: String, value: PrimitiveValue) -> NamedPrimitiveValue {
390        NamedPrimitiveValue { name, value }
391    }
392
393    /// Returns the name of this field
394    #[must_use]
395    pub fn name(&self) -> &String {
396        &self.name
397    }
398
399    /// Returns the stored value of the field
400    #[must_use]
401    pub fn value(&self) -> &PrimitiveValue {
402        &self.value
403    }
404}
405
406#[derive(Debug, Clone, PartialEq)]
407pub struct PrimitiveField {
408    pub name: String,
409    pub inner: PrimitiveFieldInner,
410}
411impl PrimitiveField {
412    pub fn new_unset(name: &str, primitive: Primitives) -> Self {
413        Self {
414            name: name.to_string(),
415            inner: PrimitiveFieldInner::Unset(primitive),
416        }
417    }
418}
419#[derive(Debug, Clone, PartialEq)]
420pub enum PrimitiveFieldInner {
421    Unset(Primitives),
422    Set(PrimitiveValue),
423}
424impl PrimitiveFieldInner {
425    pub fn primitive(&self) -> Primitives {
426        match self {
427            PrimitiveFieldInner::Unset(v) => *v,
428            PrimitiveFieldInner::Set(v) => v.primitive(),
429        }
430    }
431    pub fn value(&self) -> Option<&PrimitiveValue> {
432        if let PrimitiveFieldInner::Set(v) = self {
433            Some(v)
434        } else {
435            None
436        }
437    }
438}