hermes_five/utils/
state.rs

1use std::collections::HashMap;
2use std::fmt::{Display, Formatter};
3
4#[derive(Clone, Debug, Default, PartialEq)]
5pub enum State {
6    #[default]
7    Null,
8    Boolean(bool),
9    Integer(u64),
10    Signed(i64),
11    Float(f64),
12    String(String),
13    Array(Vec<State>),
14    Object(HashMap<String, State>),
15}
16
17impl Display for State {
18    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
19        match self {
20            State::Null => write!(f, "Null"),
21            State::Boolean(b) => write!(f, "{}", b),
22            State::Integer(i) => write!(f, "{}", i),
23            State::Signed(s) => write!(f, "{}", s),
24            State::Float(fl) => write!(f, "{}", fl),
25            State::String(s) => write!(f, "\"{}\"", s),
26            State::Array(arr) => {
27                let elements = arr
28                    .iter()
29                    .map(|e| e.to_string())
30                    .collect::<Vec<String>>()
31                    .join(", ");
32                write!(f, "[{}]", elements)
33            }
34            State::Object(obj) => {
35                let entries = obj
36                    .iter()
37                    .map(|(key, value)| format!("\"{}\": {}", key, value))
38                    .collect::<Vec<String>>()
39                    .join(", ");
40                write!(f, "{{{}}}", entries)
41            }
42        }
43    }
44}
45
46// **********************************************
47// Serde
48// **********************************************
49
50#[cfg(feature = "serde")]
51impl serde::Serialize for State {
52    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
53    where
54        S: serde::ser::Serializer,
55    {
56        match self {
57            State::Null => serializer.serialize_none(),
58            State::Boolean(b) => serializer.serialize_bool(*b),
59            State::Integer(i) => serializer.serialize_u64(*i),
60            State::Signed(i) => serializer.serialize_i64(*i),
61            State::Float(f) => serializer.serialize_f64(*f),
62            State::String(s) => serializer.serialize_str(s),
63            State::Array(a) => serializer.collect_seq(a),
64            State::Object(o) => serializer.collect_map(o),
65        }
66    }
67}
68
69#[cfg(feature = "serde")]
70impl<'de> ::serde::Deserialize<'de> for State {
71    fn deserialize<D>(de: D) -> Result<State, D::Error>
72    where
73        D: serde::de::Deserializer<'de>,
74    {
75        Ok(State::from(serde_json::Value::deserialize(de)?))
76    }
77}
78
79#[cfg(feature = "serde")]
80impl From<serde_json::Value> for State {
81    fn from(value: serde_json::Value) -> Self {
82        match value {
83            serde_json::Value::Null => State::Null,
84            serde_json::Value::Bool(b) => State::Boolean(b),
85            serde_json::Value::Number(n) => {
86                if let Some(u) = n.as_u64() {
87                    State::Integer(u)
88                } else if let Some(i) = n.as_i64() {
89                    State::Signed(i)
90                } else {
91                    State::Float(n.as_f64().unwrap())
92                }
93            }
94            serde_json::Value::String(s) => State::String(s),
95            serde_json::Value::Array(list) => {
96                State::Array(list.into_iter().map(Into::into).collect())
97            }
98            serde_json::Value::Object(map) => State::Object(
99                map.into_iter()
100                    .map(|(key, value)| (key, value.into()))
101                    .collect(),
102            ),
103        }
104    }
105}
106
107#[cfg(feature = "serde")]
108impl State {
109    pub fn into_state<T: serde::Serialize>(value: T) -> State {
110        serde_json::to_value(value).unwrap().into()
111    }
112}
113
114// **********************************************
115// Extractors: get the value inside State.
116// **********************************************
117impl State {
118    pub fn is_null(&self) -> bool {
119        *self == State::Null
120    }
121
122    /// Extracts the boolean value if it is a boolean.
123    pub fn as_bool(&self) -> bool {
124        match self {
125            State::Null => false,
126            State::Boolean(b) => *b,
127            State::Integer(u) => *u > 0,
128            State::Signed(i) => *i > 0,
129            State::Float(f) => *f > 0.0,
130            State::String(s) => !s.is_empty(),
131            State::Array(a) => !a.is_empty(),
132            State::Object(o) => !o.is_empty(),
133        }
134    }
135
136    /// Extracts the integer value if it is an integer.
137    pub fn as_integer(&self) -> u64 {
138        match *self {
139            State::Boolean(b) => u64::from(b),
140            State::Integer(u) => u,
141            State::Signed(i) => match i > 0 {
142                true => i as u64,
143                false => 0,
144            },
145            State::Float(f) => f as u64,
146            _ => 0,
147        }
148    }
149    /// Extracts the signed integer value if it is an integer.
150    pub fn as_signed_integer(&self) -> i64 {
151        match *self {
152            State::Boolean(b) => i64::from(b),
153            State::Integer(i) => i as i64,
154            State::Signed(i) => i,
155            State::Float(f) => f as i64,
156            _ => 0,
157        }
158    }
159    /// Extracts the float value if it is a float.
160    pub fn as_float(&self) -> f64 {
161        match *self {
162            State::Boolean(b) => f64::from(b),
163            State::Integer(u) => u as f64,
164            State::Signed(i) => i as f64,
165            State::Float(f) => f,
166            _ => 0.0,
167        }
168    }
169
170    /// Extracts the string of this value if it is a string.
171    pub fn as_string(&self) -> String {
172        match self {
173            State::Integer(u) => format!("{}", u),
174            State::Signed(i) => format!("{}", i),
175            State::Float(f) => format!("{}", f),
176            State::String(s) => s.clone(),
177            _ => String::default(),
178        }
179    }
180
181    /// Extracts the &str of this value if it is a string.
182    pub fn as_str(&self) -> &str {
183        match self {
184            State::String(ref s) => s,
185            _ => "",
186        }
187    }
188    /// Extracts the array value if it is an array.
189    pub fn as_array(&self) -> Vec<State> {
190        match *self {
191            State::Array(ref a) => a.clone(),
192            _ => vec![],
193        }
194    }
195    /// Extracts the hashmap value if it is a hashmap.
196    pub fn as_object(&self) -> HashMap<String, State> {
197        match self {
198            State::Object(map) => map.clone(),
199            _ => HashMap::<String, State>::default(),
200        }
201    }
202}
203
204// **********************************************
205// Converters: set a value inside State.
206// **********************************************
207
208macro_rules! impl_from_converter {
209    ($variant:ident : $T:ty) => {
210        impl From<$T> for State {
211            #[inline]
212            fn from(val: $T) -> State {
213                State::$variant(val.into())
214            }
215        }
216    };
217}
218
219impl_from_converter!(String: String);
220impl_from_converter!(Integer: u8);
221impl_from_converter!(Integer: u16);
222impl_from_converter!(Integer: u32);
223impl_from_converter!(Integer: u64);
224impl_from_converter!(Signed: i8);
225impl_from_converter!(Signed: i16);
226impl_from_converter!(Signed: i32);
227impl_from_converter!(Signed: i64);
228impl_from_converter!(Float: f32);
229impl_from_converter!(Float: f64);
230impl_from_converter!(Boolean: bool);
231// impl_from_converter!(Array: Vec<State>);
232// impl_from_converter!(Object: HashMap<String, State>);
233
234impl<T: Into<State>> From<Vec<T>> for State {
235    /// Convert a `Vec` to `State::Array`.
236    fn from(f: Vec<T>) -> Self {
237        State::Array(f.into_iter().map(Into::into).collect())
238    }
239}
240
241impl<T: Clone + Into<State>> From<&[T]> for State {
242    /// Convert a slice to `State::Array`.
243    fn from(f: &[T]) -> Self {
244        State::Array(f.iter().cloned().map(Into::into).collect())
245    }
246}
247
248impl<T: Into<State>> FromIterator<T> for State {
249    /// Create a `State::Array` by collecting an iterator of array elements.
250    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
251        State::Array(iter.into_iter().map(Into::into).collect())
252    }
253}
254
255#[cfg(test)]
256mod tests {
257    use std::collections::HashMap;
258
259    use super::*;
260
261    #[test]
262    fn test_as_boolean() {
263        assert!(!State::Null.as_bool());
264
265        assert!(!State::Boolean(false).as_bool());
266        assert!(State::Boolean(true).as_bool());
267
268        assert!(!State::Integer(0).as_bool());
269        assert!(State::Integer(10).as_bool());
270
271        assert!(!State::Signed(-10).as_bool());
272        assert!(!State::Signed(0).as_bool());
273        assert!(State::Signed(10).as_bool());
274
275        assert!(!State::Float(-0.5).as_bool());
276        assert!(!State::Float(0.0).as_bool());
277        assert!(State::Float(10.5).as_bool());
278
279        assert!(!State::String(String::from("")).as_bool());
280        assert!(State::String(" ".into()).as_bool());
281
282        assert!(!State::Array(vec!()).as_bool());
283        assert!(State::Array(vec![1.into()]).as_bool());
284
285        let mut map = HashMap::new();
286        assert!(!State::Object(map.clone()).as_bool());
287        map.insert("key".to_string(), State::Integer(42));
288        assert!(State::Object(map).as_bool());
289    }
290
291    #[test]
292    fn test_as_integer() {
293        assert_eq!(State::Null.as_integer(), 0);
294        assert_eq!(State::Boolean(false).as_integer(), 0);
295        assert_eq!(State::Boolean(true).as_integer(), 1);
296        assert_eq!(State::Integer(42).as_integer(), 42);
297        assert_eq!(State::Signed(-12).as_integer(), 0);
298        assert_eq!(State::Signed(12).as_integer(), 12);
299        assert_eq!(State::Float(69.5).as_integer(), 69);
300        assert_eq!(State::Float(-69.5).as_integer(), 0);
301        assert_eq!(State::String(String::from("test")).as_integer(), 0);
302        assert_eq!(State::Array(vec![1.into()]).as_integer(), 0);
303        assert_eq!(State::Object(HashMap::new()).as_integer(), 0);
304    }
305
306    #[test]
307    fn test_as_signed_integer() {
308        assert_eq!(State::Null.as_signed_integer(), 0);
309        assert_eq!(State::Boolean(false).as_signed_integer(), 0);
310        assert_eq!(State::Boolean(true).as_signed_integer(), 1);
311        assert_eq!(State::Integer(42).as_signed_integer(), 42);
312        assert_eq!(State::Signed(-12).as_signed_integer(), -12);
313        assert_eq!(State::Signed(12).as_signed_integer(), 12);
314        assert_eq!(State::Float(69.5).as_signed_integer(), 69);
315        assert_eq!(State::Float(-69.5).as_signed_integer(), -69);
316        assert_eq!(State::String(String::from("test")).as_signed_integer(), 0);
317        assert_eq!(State::Array(vec![1.into()]).as_signed_integer(), 0);
318        assert_eq!(State::Object(HashMap::new()).as_signed_integer(), 0);
319    }
320
321    #[test]
322    fn test_as_float() {
323        assert_eq!(State::Null.as_float(), 0.0);
324        assert_eq!(State::Boolean(false).as_float(), 0.0);
325        assert_eq!(State::Boolean(true).as_float(), 1.0);
326        assert_eq!(State::Integer(42).as_float(), 42.0);
327        assert_eq!(State::Signed(-12).as_float(), -12.0);
328        assert_eq!(State::Signed(12).as_float(), 12.0);
329        assert_eq!(State::Float(69.5).as_float(), 69.5);
330        assert_eq!(State::Float(-69.5).as_float(), -69.5);
331        assert_eq!(State::String(String::from("test")).as_float(), 0.0);
332        assert_eq!(State::Array(vec![1.into()]).as_float(), 0.0);
333        assert_eq!(State::Object(HashMap::new()).as_float(), 0.0);
334    }
335
336    #[test]
337    fn test_as_string() {
338        assert_eq!(State::Null.as_string(), String::from(""));
339        assert_eq!(State::Boolean(false).as_string(), String::from(""));
340        assert_eq!(State::Boolean(true).as_string(), String::from(""));
341        assert_eq!(State::Integer(42).as_string(), String::from("42"));
342        assert_eq!(State::Signed(-12).as_string(), String::from("-12"));
343        assert_eq!(State::Signed(12).as_string(), String::from("12"));
344        assert_eq!(State::Float(69.5).as_string(), String::from("69.5"));
345        assert_eq!(
346            State::String(String::from("test")).as_string(),
347            String::from("test")
348        );
349        assert_eq!(State::Array(vec![1.into()]).as_string(), String::from(""));
350        assert_eq!(State::Object(HashMap::new()).as_string(), String::from(""));
351    }
352
353    #[test]
354    fn test_as_str() {
355        assert_eq!(State::Null.as_str(), "");
356        assert_eq!(State::Boolean(false).as_str(), "");
357        assert_eq!(State::Boolean(true).as_str(), "");
358        assert_eq!(State::Integer(42).as_str(), "");
359        assert_eq!(State::Signed(-12).as_str(), "");
360        assert_eq!(State::Signed(12).as_str(), "");
361        assert_eq!(State::Float(69.5).as_str(), "");
362        assert_eq!(State::String(String::from("test")).as_str(), "test");
363        assert_eq!(State::Array(vec![1.into()]).as_str(), "");
364        assert_eq!(State::Object(HashMap::new()).as_str(), "");
365    }
366
367    #[test]
368    fn test_as_array() {
369        assert_eq!(State::Null.as_array(), vec![]);
370        assert_eq!(State::Boolean(false).as_array(), vec![]);
371        assert_eq!(State::Boolean(true).as_array(), vec![]);
372        assert_eq!(State::Integer(42).as_array(), vec![]);
373        assert_eq!(State::Signed(-12).as_array(), vec![]);
374        assert_eq!(State::Signed(12).as_array(), vec![]);
375        assert_eq!(State::Float(69.5).as_array(), vec![]);
376        assert_eq!(State::String(String::from("test")).as_array(), vec![]);
377        assert_eq!(
378            State::Array(vec![1u8.into(), 2u8.into()]).as_array(),
379            vec![State::Integer(1), State::Integer(2)]
380        );
381        assert_eq!(State::Object(HashMap::new()).as_array(), vec![]);
382    }
383
384    #[test]
385    fn test_as_object() {
386        let empty = HashMap::new();
387        assert_eq!(State::Null.as_object(), empty);
388        assert_eq!(State::Boolean(false).as_object(), empty);
389        assert_eq!(State::Boolean(true).as_object(), empty);
390        assert_eq!(State::Integer(42).as_object(), empty);
391        assert_eq!(State::Signed(-12).as_object(), empty);
392        assert_eq!(State::Signed(12).as_object(), empty);
393        assert_eq!(State::Float(69.5).as_object(), empty);
394        assert_eq!(State::String(String::from("test")).as_object(), empty);
395        assert_eq!(State::Array(vec![1.into()]).as_object(), empty);
396
397        let mut map = HashMap::new();
398        map.insert("key".to_string(), State::Integer(42));
399
400        let state = State::Object(map.clone());
401        assert_eq!(state.as_object(), map);
402    }
403
404    #[test]
405    fn test_is_null() {
406        assert!(State::Null.is_null());
407        assert!(!State::Boolean(false).is_null());
408        assert!(!State::Boolean(true).is_null());
409        assert!(!State::Integer(42).is_null());
410        assert!(!State::Signed(-12).is_null());
411        assert!(!State::Signed(12).is_null());
412        assert!(!State::Float(69.5).is_null());
413        assert!(!State::String(String::from("test")).is_null());
414        assert!(!State::Array(vec![1.into()]).is_null());
415        assert!(!State::Object(HashMap::new()).is_null());
416    }
417
418    #[test]
419    fn test_from_conversions() {
420        let state: State = "test".to_string().into();
421        assert_eq!(state, State::String("test".into()));
422
423        let state: State = 42u8.into();
424        assert_eq!(state, State::Integer(42));
425
426        let state: State = 42u16.into();
427        assert_eq!(state, State::Integer(42));
428
429        let state: State = 42u32.into();
430        assert_eq!(state, State::Integer(42));
431
432        let state: State = 42u64.into();
433        assert_eq!(state, State::Integer(42));
434
435        let state: State = (-42i8).into();
436        assert_eq!(state, State::Signed(-42));
437
438        let state: State = (-42i16).into();
439        assert_eq!(state, State::Signed(-42));
440
441        let state: State = (-42i32).into();
442        assert_eq!(state, State::Signed(-42));
443
444        let state: State = (-42i64).into();
445        assert_eq!(state, State::Signed(-42));
446
447        let state: State = std::f32::consts::PI.into();
448        assert!(matches!(state, State::Float(f) if (f - std::f64::consts::PI).abs() < 0.00001),);
449
450        let state: State = std::f64::consts::PI.into();
451        assert_eq!(state, State::Float(std::f64::consts::PI));
452
453        let state: State = true.into();
454        assert_eq!(state, State::Boolean(true));
455
456        let vec_state: State = vec![1u8, 2u8, 3u8].into();
457        assert_eq!(
458            vec_state,
459            State::Array(vec![
460                State::Integer(1),
461                State::Integer(2),
462                State::Integer(3)
463            ])
464        );
465
466        let state: State = [1, 2].as_slice().into();
467        assert_eq!(
468            state,
469            State::Array(vec![State::Signed(1), State::Signed(2)])
470        );
471
472        let state: State = vec![1, 2].into_iter().collect();
473        assert_eq!(
474            state,
475            State::Array(vec![State::Signed(1), State::Signed(2)])
476        );
477    }
478
479    #[test]
480    fn test_display_null() {
481        let state = State::Null;
482        assert_eq!(state.to_string(), "Null");
483    }
484
485    #[test]
486    fn test_display_boolean() {
487        let state_true = State::Boolean(true);
488        let state_false = State::Boolean(false);
489        assert_eq!(state_true.to_string(), "true");
490        assert_eq!(state_false.to_string(), "false");
491    }
492
493    #[test]
494    fn test_display_integer() {
495        let state = State::Integer(42);
496        assert_eq!(state.to_string(), "42");
497    }
498
499    #[test]
500    fn test_display_signed() {
501        let state_positive = State::Signed(42);
502        let state_negative = State::Signed(-42);
503        assert_eq!(state_positive.to_string(), "42");
504        assert_eq!(state_negative.to_string(), "-42");
505    }
506
507    #[test]
508    fn test_display_float() {
509        #[allow(clippy::approx_constant)]
510        let state = State::Float(3.14);
511        assert_eq!(state.to_string(), "3.14");
512    }
513
514    #[test]
515    fn test_display_string() {
516        let state = State::String("Hello".to_string());
517        assert_eq!(state.to_string(), "\"Hello\"");
518    }
519
520    #[test]
521    fn test_display_array() {
522        let state = State::Array(vec![
523            State::Integer(1),
524            State::Boolean(false),
525            State::String("test".to_string()),
526        ]);
527        assert_eq!(state.to_string(), "[1, false, \"test\"]");
528    }
529
530    #[test]
531    fn test_display_object() {
532        let mut obj = HashMap::new();
533        obj.insert("key1".to_string(), State::Integer(10));
534        obj.insert("key2".to_string(), State::Boolean(true));
535
536        let state = State::Object(obj);
537        // Since HashMap does not guarantee ordering, we'll check both possible orderings
538        let result = state.to_string();
539        let expected1 = "{\"key1\": 10, \"key2\": true}";
540        let expected2 = "{\"key2\": true, \"key1\": 10}";
541
542        assert!(result == expected1 || result == expected2);
543    }
544
545    #[cfg(feature = "serde")]
546    #[cfg(test)]
547    mod serde_tests {
548        use serde_json;
549
550        use super::*;
551
552        #[test]
553        fn test_serialize_null() {
554            let state = State::Null;
555            let json = serde_json::to_string(&state).unwrap();
556            assert_eq!(json, "null");
557        }
558
559        #[test]
560        fn test_serialize_boolean() {
561            let state = State::Boolean(true);
562            let json = serde_json::to_string(&state).unwrap();
563            assert_eq!(json, "true");
564        }
565
566        #[test]
567        fn test_serialize_integer() {
568            let state = State::Integer(42);
569            let json = serde_json::to_string(&state).unwrap();
570            assert_eq!(json, "42");
571        }
572
573        #[test]
574        fn test_serialize_signed() {
575            let state = State::Signed(-42);
576            let json = serde_json::to_string(&state).unwrap();
577            assert_eq!(json, "-42");
578        }
579
580        #[test]
581        fn test_serialize_float() {
582            let state = State::Float(42.42);
583            let json = serde_json::to_string(&state).unwrap();
584            assert_eq!(json, "42.42");
585        }
586
587        #[test]
588        fn test_serialize_string() {
589            let state = State::String("test".into());
590            let json = serde_json::to_string(&state).unwrap();
591            assert_eq!(json, r#""test""#);
592        }
593
594        #[test]
595        fn test_serialize_array() {
596            let state = State::Array(vec![State::Integer(1), State::Integer(2)]);
597            let json = serde_json::to_string(&state).unwrap();
598            assert_eq!(json, "[1,2]");
599        }
600
601        #[test]
602        fn test_serialize_object() {
603            let mut map = HashMap::new();
604            map.insert("key".to_string(), State::Integer(42));
605            let state = State::Object(map);
606            let json = serde_json::to_string(&state).unwrap();
607            assert_eq!(json, r#"{"key":42}"#);
608        }
609
610        #[test]
611        fn test_deserialize_null() {
612            let json = "null";
613            let state: State = serde_json::from_str(json).unwrap();
614            assert_eq!(state, State::Null);
615        }
616
617        #[test]
618        fn test_deserialize_boolean() {
619            let json = "true";
620            let state: State = serde_json::from_str(json).unwrap();
621            assert_eq!(state, State::Boolean(true));
622        }
623
624        #[test]
625        fn test_deserialize_integer() {
626            let json = "42";
627            let state: State = serde_json::from_str(json).unwrap();
628            assert_eq!(state, State::Integer(42));
629        }
630
631        #[test]
632        fn test_deserialize_signed() {
633            let json = "-42";
634            let state: State = serde_json::from_str(json).unwrap();
635            assert_eq!(state, State::Signed(-42));
636        }
637
638        #[test]
639        fn test_deserialize_float() {
640            let json = "42.42";
641            let state: State = serde_json::from_str(json).unwrap();
642            assert_eq!(state, State::Float(42.42));
643        }
644
645        #[test]
646        fn test_deserialize_nan() {
647            let json = serde_json::to_string(&(f64::NAN)).unwrap();
648            let state: State = serde_json::from_str(json.as_str()).unwrap();
649            assert_eq!(state, State::Null);
650        }
651
652        #[test]
653        fn test_deserialize_string() {
654            let json = r#""test""#;
655            let state: State = serde_json::from_str(json).unwrap();
656            assert_eq!(state, State::String("test".into()));
657        }
658
659        #[test]
660        fn test_deserialize_array() {
661            let json = "[1,2]";
662            let state: State = serde_json::from_str(json).unwrap();
663            assert_eq!(
664                state,
665                State::Array(vec![State::Integer(1), State::Integer(2)])
666            );
667        }
668
669        #[test]
670        fn test_deserialize_object() {
671            let json = r#"{"key":42}"#;
672            let state: State = serde_json::from_str(json).unwrap();
673            let mut map = HashMap::new();
674            map.insert("key".to_string(), State::Integer(42));
675            assert_eq!(state, State::Object(map));
676        }
677
678        #[test]
679        fn test_deserialize_complex() {
680            let json = r#"{"key":42, "state": {"key": 42}}"#;
681            let state: State = serde_json::from_str(json).unwrap();
682            let mut map = HashMap::new();
683            map.insert("key".to_string(), State::Integer(42));
684            map.insert("state".to_string(), State::Object(map.clone()));
685            assert_eq!(state, State::Object(map));
686        }
687
688        #[test]
689        fn test_into_state() {
690            let input = "hello world";
691            let state = State::into_state(input);
692            assert_eq!(state.as_string(), String::from("hello world"));
693        }
694    }
695}