entity/ent/value/
primitive.rs

1use super::{Number, NumberLike, NumberType};
2
3use std::{
4    cmp::Ordering,
5    convert::TryFrom,
6    hash::{Hash, Hasher},
7};
8use strum::ParseError;
9
10/// Represents a primitive value
11#[derive(Copy, Clone, Debug)]
12#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
13pub enum Primitive {
14    Bool(bool),
15    Char(char),
16    Number(Number),
17    Unit,
18}
19
20impl Default for PrimitiveType {
21    /// Returns default primitive value type of unit
22    fn default() -> Self {
23        Self::Unit
24    }
25}
26
27impl Primitive {
28    /// Returns true if this value is of the specified type
29    #[inline]
30    pub fn is_type(&self, r#type: PrimitiveType) -> bool {
31        self.to_type() == r#type
32    }
33
34    /// Returns the type of this value
35    #[inline]
36    pub fn to_type(&self) -> PrimitiveType {
37        PrimitiveType::from(self)
38    }
39
40    /// Returns true if this value and the other value are of the same type
41    #[inline]
42    pub fn has_same_type(&self, other: &Primitive) -> bool {
43        self.to_type() == other.to_type()
44    }
45}
46
47impl Hash for Primitive {
48    fn hash<H: Hasher>(&self, state: &mut H) {
49        match self {
50            Self::Bool(x) => x.hash(state),
51            Self::Char(x) => x.hash(state),
52            Self::Number(x) => x.hash(state),
53            Self::Unit => Self::Unit.hash(state),
54        }
55    }
56}
57
58/// Value is considered equal, ignoring the fact that NaN != NaN for floats
59impl Eq for Primitive {}
60
61impl PartialEq for Primitive {
62    /// Compares two primitive values of same type for equality, otherwise
63    /// returns false
64    fn eq(&self, other: &Self) -> bool {
65        match (self, other) {
66            (Self::Bool(a), Self::Bool(b)) => a == b,
67            (Self::Char(a), Self::Char(b)) => a == b,
68            (Self::Number(a), Self::Number(b)) => a == b,
69            (Self::Unit, Self::Unit) => true,
70            _ => false,
71        }
72    }
73}
74
75impl PartialOrd for Primitive {
76    /// Compares same variants of same type for ordering, otherwise returns none
77    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
78        match (self, other) {
79            (Self::Bool(a), Self::Bool(b)) => a.partial_cmp(b),
80            (Self::Char(a), Self::Char(b)) => a.partial_cmp(b),
81            (Self::Number(a), Self::Number(b)) => a.partial_cmp(b),
82            (Self::Unit, Self::Unit) => Some(Ordering::Equal),
83            _ => None,
84        }
85    }
86}
87
88/// Represents some data that can be converted to and from a [`Primitive`]
89pub trait PrimitiveLike: Sized {
90    /// Consumes this data, converting it into an abstract [`Primitive`]
91    fn into_primitive(self) -> Primitive;
92
93    /// Attempts to convert an abstract [`Primitive`] into this data, returning
94    /// the owned value back if unable to convert
95    fn try_from_primitive(primitive: Primitive) -> Result<Self, Primitive>;
96}
97
98impl PrimitiveLike for Primitive {
99    fn into_primitive(self) -> Primitive {
100        self
101    }
102
103    fn try_from_primitive(primitive: Primitive) -> Result<Self, Primitive> {
104        Ok(primitive)
105    }
106}
107
108impl PrimitiveLike for bool {
109    fn into_primitive(self) -> Primitive {
110        Primitive::Bool(self)
111    }
112
113    fn try_from_primitive(primitive: Primitive) -> Result<Self, Primitive> {
114        match primitive {
115            Primitive::Bool(x) => Ok(x),
116            x => Err(x),
117        }
118    }
119}
120
121impl PrimitiveLike for char {
122    fn into_primitive(self) -> Primitive {
123        Primitive::Char(self)
124    }
125
126    fn try_from_primitive(primitive: Primitive) -> Result<Self, Primitive> {
127        match primitive {
128            Primitive::Char(x) => Ok(x),
129            x => Err(x),
130        }
131    }
132}
133
134impl<T: NumberLike> PrimitiveLike for T {
135    fn into_primitive(self) -> Primitive {
136        Primitive::Number(self.into_number())
137    }
138
139    fn try_from_primitive(primitive: Primitive) -> Result<Self, Primitive> {
140        match primitive {
141            Primitive::Number(x) => T::try_from_number(x).map_err(Primitive::Number),
142            x => Err(x),
143        }
144    }
145}
146
147impl PrimitiveLike for () {
148    fn into_primitive(self) -> Primitive {
149        Primitive::Unit
150    }
151
152    fn try_from_primitive(primitive: Primitive) -> Result<Self, Primitive> {
153        match primitive {
154            Primitive::Unit => Ok(()),
155            x => Err(x),
156        }
157    }
158}
159
160impl From<()> for Primitive {
161    fn from(_: ()) -> Self {
162        Self::Unit
163    }
164}
165
166impl TryFrom<Primitive> for () {
167    type Error = Primitive;
168
169    fn try_from(x: Primitive) -> Result<Self, Self::Error> {
170        PrimitiveLike::try_from_primitive(x)
171    }
172}
173
174macro_rules! impl_conv {
175    ($($type:ty)+) => {$(
176        impl From<$type> for Primitive {
177            fn from(x: $type) -> Self {
178                <$type as PrimitiveLike>::into_primitive(x)
179            }
180        }
181
182        impl TryFrom<Primitive> for $type {
183            type Error = Primitive;
184
185            fn try_from(x: Primitive) -> Result<Self, Self::Error> {
186                <$type as PrimitiveLike>::try_from_primitive(x)
187            }
188        }
189    )+};
190}
191
192impl_conv!(bool char f32 f64 i128 i16 i32 i64 i8 isize u128 u16 u32 u64 u8 usize);
193
194/// Represents primitive value types
195#[derive(Copy, Clone, Debug, PartialEq, Eq)]
196#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
197pub enum PrimitiveType {
198    Bool,
199    Char,
200    Number(NumberType),
201    Unit,
202}
203
204impl PrimitiveType {
205    pub fn is_bool(&self) -> bool {
206        matches!(self, Self::Bool)
207    }
208
209    pub fn is_char(&self) -> bool {
210        matches!(self, Self::Char)
211    }
212
213    pub fn is_number(&self) -> bool {
214        matches!(self, Self::Number(_))
215    }
216
217    pub fn is_unit(&self) -> bool {
218        matches!(self, Self::Unit)
219    }
220
221    pub fn to_number_type(&self) -> Option<NumberType> {
222        match self {
223            Self::Number(x) => Some(*x),
224            _ => None,
225        }
226    }
227
228    /// Constructs a primitive value type from a Rust-based type string similar
229    /// to what you would find from `std::any::type_name`
230    ///
231    /// ## Examples
232    ///
233    /// ```
234    /// use entity::{PrimitiveType as PVT, NumberType as NT};
235    ///
236    /// assert_eq!(
237    ///     PVT::from_type_name("bool").unwrap(),
238    ///     PVT::Bool,
239    /// );
240    ///
241    /// assert_eq!(
242    ///     PVT::from_type_name("char").unwrap(),
243    ///     PVT::Char,
244    /// );
245    ///
246    /// assert_eq!(
247    ///     PVT::from_type_name("u8").unwrap(),
248    ///     PVT::Number(NT::U8),
249    /// );
250    ///
251    /// assert_eq!(
252    ///     PVT::from_type_name("()").unwrap(),
253    ///     PVT::Unit,
254    /// );
255    /// ```
256    pub fn from_type_name(tname: &str) -> Result<Self, ParseError> {
257        use std::str::FromStr;
258
259        // Translate any Rust-specific types to our custom format, passing
260        // anything that is the same to our FromStr implementation
261        match tname {
262            "()" => Self::from_str("unit"),
263            x => Self::from_str(x),
264        }
265    }
266}
267
268impl std::fmt::Display for PrimitiveType {
269    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
270        match self {
271            Self::Bool => write!(f, "bool"),
272            Self::Char => write!(f, "char"),
273            Self::Number(t) => write!(f, "number:{}", t),
274            Self::Unit => write!(f, "unit"),
275        }
276    }
277}
278
279impl From<Primitive> for PrimitiveType {
280    fn from(v: Primitive) -> Self {
281        Self::from(&v)
282    }
283}
284
285impl<'a> From<&'a Primitive> for PrimitiveType {
286    fn from(v: &'a Primitive) -> Self {
287        match v {
288            Primitive::Bool(_) => Self::Bool,
289            Primitive::Char(_) => Self::Char,
290            Primitive::Number(x) => Self::Number(x.to_type()),
291            Primitive::Unit => Self::Unit,
292        }
293    }
294}
295
296impl std::str::FromStr for PrimitiveType {
297    type Err = ParseError;
298
299    /// Parses a primitive value type
300    ///
301    /// ## Examples
302    ///
303    /// ```
304    /// use entity::{PrimitiveType as PVT, NumberType as NT};
305    /// use strum::ParseError;
306    /// use std::str::FromStr;
307    ///
308    /// assert_eq!(PVT::from_str("bool").unwrap(), PVT::Bool);
309    /// assert_eq!(PVT::from_str("char").unwrap(), PVT::Char);
310    /// assert_eq!(PVT::from_str("u32").unwrap(), PVT::Number(NT::U32));
311    /// assert_eq!(PVT::from_str("number:u32").unwrap(), PVT::Number(NT::U32));
312    /// assert_eq!(PVT::from_str("unit").unwrap(), PVT::Unit);
313    /// assert_eq!(PVT::from_str("unknown").unwrap_err(), ParseError::VariantNotFound);
314    /// ```
315    fn from_str(s: &str) -> Result<Self, Self::Err> {
316        let mut s_it = s.split(':');
317        let primary = s_it.next();
318        let secondary = s_it.next();
319        let has_more = s_it.next().is_some();
320
321        // If has too many values, we exit
322        if has_more {
323            return Err(ParseError::VariantNotFound);
324        }
325
326        match (primary, secondary) {
327            (Some("bool"), None) => Ok(Self::Bool),
328            (Some("char"), None) => Ok(Self::Char),
329            (Some("number"), Some(x)) => Ok(Self::Number(NumberType::from_str(x)?)),
330            (Some("unit"), None) => Ok(Self::Unit),
331            (Some(x), None) => Ok(Self::Number(NumberType::from_str(x)?)),
332            _ => Err(ParseError::VariantNotFound),
333        }
334    }
335}
336
337#[cfg(test)]
338mod tests {
339    use super::*;
340
341    #[test]
342    fn primitive_like_can_convert_number_like_to_primitive() {
343        assert!(matches!(
344            1u8.into_primitive(),
345            Primitive::Number(Number::U8(1)),
346        ));
347    }
348
349    #[test]
350    fn primitive_like_can_convert_primitive_to_number_like() {
351        assert!(matches!(
352            Number::try_from_primitive(Primitive::Number(Number::U8(1))),
353            Ok(Number::U8(1)),
354        ));
355
356        assert!(matches!(
357            Number::try_from_primitive(Primitive::Char('c')),
358            Err(Primitive::Char('c')),
359        ));
360    }
361
362    #[test]
363    fn primitive_like_can_convert_bool_to_primitive() {
364        assert!(matches!(true.into_primitive(), Primitive::Bool(true),));
365    }
366
367    #[test]
368    fn primitive_like_can_convert_primitive_to_bool() {
369        assert!(matches!(
370            bool::try_from_primitive(Primitive::Bool(true)),
371            Ok(true)
372        ));
373
374        assert!(matches!(
375            bool::try_from_primitive(Primitive::Char('c')),
376            Err(Primitive::Char('c')),
377        ));
378    }
379
380    #[test]
381    fn primitive_like_can_convert_char_to_primitive() {
382        assert!(matches!('c'.into_primitive(), Primitive::Char('c')));
383    }
384
385    #[test]
386    fn primitive_like_can_convert_primitive_to_char() {
387        assert!(matches!(
388            char::try_from_primitive(Primitive::Char('c')),
389            Ok('c')
390        ));
391
392        assert!(matches!(
393            char::try_from_primitive(Primitive::Bool(true)),
394            Err(Primitive::Bool(true)),
395        ));
396    }
397
398    #[test]
399    fn primitive_like_can_convert_unit_to_primitive() {
400        assert!(matches!(().into_primitive(), Primitive::Unit));
401    }
402
403    #[test]
404    fn primitive_like_can_convert_primitive_to_unit() {
405        assert!(matches!(<()>::try_from_primitive(Primitive::Unit), Ok(())));
406
407        assert!(matches!(
408            <()>::try_from_primitive(Primitive::Bool(true)),
409            Err(Primitive::Bool(true)),
410        ));
411    }
412}