tau_engine/
value.rs

1use std::borrow::Cow;
2use std::collections::{HashMap, HashSet};
3use std::hash::BuildHasher;
4
5/// A dynamic data type that the solver can reason on.
6#[derive(Clone)]
7pub enum Value<'a> {
8    /// Represents an empty type.
9    Null,
10    /// Represents a boolean.
11    Bool(bool),
12    /// Represents a float.
13    Float(f64),
14    /// Represents an integer.
15    Int(i64),
16    /// Represents an unsigned integer.
17    UInt(u64),
18    /// Represents a string.
19    String(Cow<'a, str>),
20    /// Represents an array.
21    Array(&'a dyn Array),
22    /// Represents an object.
23    Object(&'a dyn Object),
24}
25
26impl<'a> Value<'a> {
27    /// Returns true if the `Value` is an Array.
28    #[inline]
29    pub fn is_array(&self) -> bool {
30        matches!(self, Self::Array(_))
31    }
32
33    /// Returns true if the `Value` is a Bool.
34    #[inline]
35    pub fn is_bool(&self) -> bool {
36        matches!(self, Self::Bool(_))
37    }
38
39    /// Returns true if the `Value` is a Float.
40    #[inline]
41    pub fn is_f64(&self) -> bool {
42        matches!(self, Self::Float(_))
43    }
44
45    /// Returns true if the `Value` is an Int.
46    #[inline]
47    pub fn is_i64(&self) -> bool {
48        matches!(self, Self::Int(_))
49    }
50
51    /// Returns true if the `Value` is a Null.
52    #[inline]
53    pub fn is_null(&self) -> bool {
54        matches!(self, Self::Null)
55    }
56
57    /// Returns true if the `Value` is an Object.
58    #[inline]
59    pub fn is_object(&self) -> bool {
60        matches!(self, Self::Object(_))
61    }
62
63    /// Returns true if the `Value` is a String.
64    #[inline]
65    pub fn is_string(&self) -> bool {
66        matches!(self, Self::String(_))
67    }
68
69    /// Returns true if the `Value` is a UInt.
70    #[inline]
71    pub fn is_u64(&self) -> bool {
72        matches!(self, Self::UInt(_))
73    }
74
75    /// Return the associated array if the `Value` is an Array.
76    #[inline]
77    pub fn as_array(&self) -> Option<&dyn Array> {
78        match self {
79            Self::Array(a) => Some(*a),
80            _ => None,
81        }
82    }
83
84    /// Return the associated boolean if the `Value` is a Bool.
85    #[inline]
86    pub fn as_bool(&self) -> Option<bool> {
87        match self {
88            Self::Bool(b) => Some(*b),
89            _ => None,
90        }
91    }
92
93    /// Return the associated f64 if the `Value` is a Float.
94    #[inline]
95    pub fn as_f64(&self) -> Option<f64> {
96        match self {
97            Self::Float(n) => Some(*n),
98            _ => None,
99        }
100    }
101
102    /// Return the associated i64 if the `Value` is a Int.
103    #[inline]
104    pub fn as_i64(&self) -> Option<i64> {
105        match self {
106            Self::Int(n) => Some(*n),
107            _ => None,
108        }
109    }
110
111    /// Return the associated () if the `Value` is a Null.
112    #[inline]
113    pub fn as_null(&self) -> Option<()> {
114        match self {
115            Self::Null => Some(()),
116            _ => None,
117        }
118    }
119
120    /// Return the associated object if the `Value` is an Object.
121    #[inline]
122    pub fn as_object(&self) -> Option<&dyn Object> {
123        match self {
124            Self::Object(o) => Some(*o),
125            _ => None,
126        }
127    }
128
129    /// Return the associated str if the `Value` is a String.
130    #[inline]
131    pub fn as_str(&self) -> Option<&str> {
132        match self {
133            Self::String(s) => Some(s),
134            _ => None,
135        }
136    }
137
138    /// Return the associated u64 if the `Value` is a UInt.
139    #[inline]
140    pub fn as_u64(&self) -> Option<u64> {
141        match self {
142            Self::UInt(n) => Some(*n),
143            _ => None,
144        }
145    }
146
147    /// Returns the `Value` as an i64 if possible.
148    ///
149    /// Currently supports: Int & UInt.
150    #[inline]
151    pub fn to_i64(&self) -> Option<i64> {
152        match self {
153            Self::Int(n) => Some(*n),
154            Self::UInt(n) => {
155                if *n <= i64::MAX as u64 {
156                    Some(*n as i64)
157                } else {
158                    None
159                }
160            }
161            _ => None,
162        }
163    }
164
165    /// Returns the `Value` as a String if possible.
166    ///
167    /// Currently supports: Bool, Float, Int, String & UInt.
168    #[inline]
169    pub fn to_string(&self) -> Option<String> {
170        match self {
171            Self::Bool(b) => Some(b.to_string()),
172            Self::Int(i) => Some(i.to_string()),
173            Self::UInt(u) => Some(u.to_string()),
174            Self::Float(f) => Some(f.to_string()),
175            Self::String(s) => Some(s.to_string()),
176            _ => None,
177        }
178    }
179}
180
181/// A **data type** that can be represented as a `Value`.
182///
183/// # Implementations
184///
185/// As long as the **data type** can be coerced into one of the values provided by `Value` then
186/// `AsValue` can be implemented on that type. Below is a contrived example:
187///
188/// ```
189/// use std::borrow::Cow;
190///
191/// use tau_engine::{AsValue, Value};
192///
193/// enum Foo {
194///     Bar,
195///     Baz
196/// }
197///
198/// impl AsValue for Foo {
199///     fn as_value(&self) -> Value<'_> {
200///         match self {
201///             Self::Bar => Value::String(Cow::Borrowed("bar")),
202///             Self::Baz => Value::String(Cow::Borrowed("baz")),
203///         }
204///     }
205/// }
206/// ```
207#[cfg(not(feature = "sync"))]
208pub trait AsValue {
209    /// Returns the implemented type as a `Value`
210    ///
211    /// # Example
212    ///
213    /// ```
214    /// use tau_engine::AsValue;
215    ///
216    /// let value = "foobar".as_value();
217    /// ```
218    fn as_value(&self) -> Value<'_>;
219}
220#[cfg(feature = "sync")]
221pub trait AsValue: Send + Sync {
222    fn as_value(&self) -> Value<'_>;
223}
224
225impl AsValue for () {
226    #[inline]
227    fn as_value(&self) -> Value<'_> {
228        Value::Null
229    }
230}
231
232impl AsValue for bool {
233    #[inline]
234    fn as_value(&self) -> Value<'_> {
235        Value::Bool(*self)
236    }
237}
238
239impl AsValue for str {
240    #[inline]
241    fn as_value(&self) -> Value<'_> {
242        Value::String(Cow::Borrowed(self))
243    }
244}
245
246impl AsValue for String {
247    #[inline]
248    fn as_value(&self) -> Value<'_> {
249        Value::String(Cow::Borrowed(self))
250    }
251}
252
253impl<V> AsValue for HashSet<V>
254where
255    V: AsValue,
256{
257    #[inline]
258    fn as_value(&self) -> Value<'_> {
259        Value::Array(self)
260    }
261}
262
263impl<V> AsValue for Option<V>
264where
265    V: AsValue,
266{
267    #[inline]
268    fn as_value(&self) -> Value<'_> {
269        self.as_ref().map(|v| v.as_value()).unwrap_or(Value::Null)
270    }
271}
272
273impl<V> AsValue for Vec<V>
274where
275    V: AsValue,
276{
277    #[inline]
278    fn as_value(&self) -> Value<'_> {
279        Value::Array(self)
280    }
281}
282
283macro_rules! impl_as_value_float {
284    ($ty:ty) => {
285        impl AsValue for $ty {
286            #[inline]
287            fn as_value(&self) -> Value<'_> {
288                Value::Float(*self as f64)
289            }
290        }
291    };
292}
293
294impl_as_value_float!(f32);
295impl_as_value_float!(f64);
296
297macro_rules! impl_as_value_int {
298    ($ty:ty) => {
299        impl AsValue for $ty {
300            #[inline]
301            fn as_value(&self) -> Value<'_> {
302                Value::Int(*self as i64)
303            }
304        }
305    };
306}
307
308impl_as_value_int!(i8);
309impl_as_value_int!(i16);
310impl_as_value_int!(i32);
311impl_as_value_int!(i64);
312impl_as_value_int!(isize);
313
314macro_rules! impl_as_value_uint {
315    ($ty:ty) => {
316        impl AsValue for $ty {
317            #[inline]
318            fn as_value(&self) -> Value<'_> {
319                Value::UInt(*self as u64)
320            }
321        }
322    };
323}
324
325impl_as_value_uint!(u8);
326impl_as_value_uint!(u16);
327impl_as_value_uint!(u32);
328impl_as_value_uint!(u64);
329impl_as_value_uint!(usize);
330
331/// A **data type** that can be represented as an `Array`.
332///
333/// This allows more complex array-like data types to be represented in a generic way for use as a
334/// `Value`.
335///
336/// # Implementations
337///
338/// As long as the **data type** is considered array-like then `Array` can be implemented on that
339/// type. Below is a contrived example:
340///
341/// ```
342/// use tau_engine::{Array, Value};
343///
344/// // NOTE: Implements Iterator
345/// #[derive(Clone)]
346/// struct Counter {
347///    count: usize,
348/// }
349/// # impl Iterator for Counter {
350/// #    // we will be counting with usize
351/// #    type Item = usize;
352///
353/// #    // next() is the only required method
354/// #    fn next(&mut self) -> Option<Self::Item> {
355/// #        // Increment our count. This is why we started at zero.
356/// #        self.count += 1;
357///
358/// #        // Check to see if we've finished counting or not.
359/// #        if self.count < 6 {
360/// #            Some(self.count)
361/// #        } else {
362/// #            None
363/// #        }
364/// #    }
365/// # }
366/// impl Array for Counter {
367///     fn iter(&self) -> Box<dyn Iterator<Item = Value<'_>> + '_> {
368///         Box::new(self.clone().map(|v| Value::UInt(v as u64)))
369///     }
370///
371///     fn len(&self) -> usize {
372///         self.clone().count()
373///     }
374/// }
375/// ```
376#[allow(clippy::len_without_is_empty)]
377#[cfg(not(feature = "sync"))]
378pub trait Array {
379    /// Returns a boxed iterator of `Value` items.
380    ///
381    /// # Example
382    ///
383    /// ```
384    /// use std::collections::HashSet;
385    /// use tau_engine::{Array, Value};
386    ///
387    /// let mut set = HashSet::new();
388    /// set.insert(1);
389    ///
390    /// let mut value = Array::iter(&set);
391    ///
392    /// assert_eq!(value.next().is_some(), true);
393    /// ```
394    fn iter(&self) -> Box<dyn Iterator<Item = Value<'_>> + '_>;
395
396    /// Returns the length of the array.
397    ///
398    /// # Example
399    ///
400    ///```
401    /// use std::collections::HashSet;
402    /// use tau_engine::{Array, Value};
403    ///
404    /// let mut set = HashSet::new();
405    /// set.insert(1);
406    ///
407    /// let len = Array::len(&set);
408    ///
409    /// assert_eq!(len, 1);
410    /// ```
411    fn len(&self) -> usize;
412}
413#[cfg(feature = "sync")]
414pub trait Array: Send + Sync {
415    fn iter(&self) -> Box<dyn Iterator<Item = Value<'_>> + '_>;
416    fn len(&self) -> usize;
417}
418
419impl<V> Array for HashSet<V>
420where
421    V: AsValue,
422{
423    #[inline]
424    fn iter(&self) -> Box<dyn Iterator<Item = Value<'_>> + '_> {
425        Box::new(self.iter().map(|v| v.as_value()))
426    }
427
428    #[inline]
429    fn len(&self) -> usize {
430        self.len()
431    }
432}
433
434impl<V> Array for Vec<V>
435where
436    V: AsValue,
437{
438    #[inline]
439    fn iter(&self) -> Box<dyn Iterator<Item = Value<'_>> + '_> {
440        Box::new(self.as_slice().iter().map(|v| v.as_value()))
441    }
442
443    #[inline]
444    fn len(&self) -> usize {
445        self.len()
446    }
447}
448
449/// A **data type** that can be represented as an `Object`.
450///
451/// This allows more complex object-like data types to be represented in a generic way for use as a
452/// `Value`.
453///
454/// # Implementations
455///
456/// As long as the **data type** is considered object-like then `Object` can be implemented on that
457/// type. Below is a contrived example:
458///j
459/// ```
460/// use std::borrow::Cow;
461///
462/// use tau_engine::{Object, Value};
463///
464/// struct Foo {
465///     pub bar: String,
466///     pub baz: String,
467/// }
468///
469/// impl Object for Foo {
470///     fn get(&self, key: &str) -> Option<Value<'_>> {
471///         match key {
472///             "bar" => Some(Value::String(Cow::Borrowed(&self.bar))),
473///             "baz" => Some(Value::String(Cow::Borrowed(&self.baz))),
474///             _ => None,
475///         }
476///     }
477///
478///     fn keys(&self) -> Vec<Cow<'_, str>> {
479///         ["bar", "baz"].iter().map(|s| Cow::Borrowed(*s)).collect()
480///     }
481///
482///     fn len(&self) -> usize {
483///         2
484///     }
485/// }
486/// ```
487///
488/// # Find
489///
490/// The `find` function allows for nested access from an `Object`. A default implementation is
491/// provided by the trait which assumes the key will split on the `.` character. This can be overriden if
492/// required. Below is an example of how find works for a complex data structure.
493///
494/// ```
495/// # use std::borrow::Cow;
496/// # use tau_engine::Value;
497/// use tau_engine::Object;
498///
499/// struct Foo {
500///     pub bar: String,
501/// }
502/// # impl Object for Foo {
503/// #     fn get(&self, key: &str) -> Option<Value<'_>> {
504/// #         match key {
505/// #             "bar" => Some(Value::String(Cow::Borrowed(&self.bar))),
506/// #             _ => None,
507/// #         }
508/// #     }
509/// #
510/// #     fn keys(&self) -> Vec<Cow<'_, str>> {
511/// #         ["bar"].iter().map(|s| Cow::Borrowed(*s)).collect()
512/// #     }
513/// #
514/// #     fn len(&self) -> usize {
515/// #         1
516/// #     }
517/// # }
518/// struct Baz {
519///     pub foo: Foo,
520/// }
521/// # impl Object for Baz {
522/// #     fn get(&self, key: &str) -> Option<Value<'_>> {
523/// #         match key {
524/// #             "foo" => Some(Value::Object(&self.foo)),
525/// #             _ => None,
526/// #         }
527/// #     }
528/// #
529/// #     fn keys(&self) -> Vec<Cow<'_, str>> {
530/// #         ["foo"].iter().map(|s| Cow::Borrowed(*s)).collect()
531/// #     }
532/// #
533/// #     fn len(&self) -> usize {
534/// #         1
535/// #     }
536/// # }
537/// let complex = Baz {
538///     foo: Foo {
539///         bar: "foobar".to_owned(),
540///     }
541/// };
542///
543/// let value = complex.find("foo.bar").unwrap();
544///
545/// assert_eq!(value.as_str(), Some("foobar"));
546/// ```
547#[allow(clippy::len_without_is_empty)]
548#[cfg(not(feature = "sync"))]
549pub trait Object {
550    /// Looks for a `Value` by key and returns it if found. The provided implementation will split
551    /// the key on `.` to handle nesting.
552    fn find(&self, key: &str) -> Option<Value<'_>> {
553        let mut v: Option<Value<'_>> = None;
554        for k in key.split('.') {
555            if k.ends_with(']') && k.contains('[') {
556                let mut parts = k.split('[');
557                let k = parts.next().expect("missing key");
558                let i: usize = match parts
559                    .next()
560                    .and_then(|i| i.strip_suffix("]"))
561                    .and_then(|i| i.parse::<usize>().ok())
562                {
563                    Some(i) => i,
564                    None => return None,
565                };
566                match v {
567                    Some(Value::Object(value)) => match value.get(k) {
568                        Some(Value::Array(a)) => v = a.iter().nth(i),
569                        _ => return None,
570                    },
571                    Some(_) => return None,
572                    None => match <Self as Object>::get(self, k) {
573                        Some(Value::Array(a)) => v = a.iter().nth(i),
574                        _ => return None,
575                    },
576                }
577            } else {
578                match v {
579                    Some(Value::Object(value)) => v = value.get(k),
580                    Some(_) => return None,
581                    None => match <Self as Object>::get(self, k) {
582                        Some(value) => v = Some(value),
583                        None => return None,
584                    },
585                }
586            }
587        }
588        v
589    }
590
591    /// Get the `Value` corresponding to the key.
592    fn get(&self, key: &str) -> Option<Value<'_>>;
593
594    /// Returns the keys for the object.
595    fn keys(&self) -> Vec<Cow<'_, str>>;
596
597    /// Returns the number of elements in the object.
598    fn len(&self) -> usize;
599}
600#[cfg(feature = "sync")]
601pub trait Object: Send + Sync {
602    fn find(&self, key: &str) -> Option<Value<'_>> {
603        let mut v: Option<Value<'_>> = None;
604        for k in key.split('.') {
605            if k.ends_with(']') && k.contains('[') {
606                let mut parts = k.split('[');
607                let k = parts.next().expect("missing key");
608                let i: usize = match parts
609                    .next()
610                    .and_then(|i| i.strip_suffix("]"))
611                    .and_then(|i| i.parse::<usize>().ok())
612                {
613                    Some(i) => i,
614                    None => return None,
615                };
616                match v {
617                    Some(Value::Object(value)) => match value.get(k) {
618                        Some(Value::Array(a)) => v = a.iter().nth(i),
619                        _ => return None,
620                    },
621                    Some(_) => return None,
622                    None => match <Self as Object>::get(self, k) {
623                        Some(Value::Array(a)) => v = a.iter().nth(i),
624                        _ => return None,
625                    },
626                }
627            } else {
628                match v {
629                    Some(Value::Object(value)) => v = value.get(k),
630                    Some(_) => return None,
631                    None => match <Self as Object>::get(self, k) {
632                        Some(value) => v = Some(value),
633                        None => return None,
634                    },
635                }
636            }
637        }
638        v
639    }
640    fn get(&self, key: &str) -> Option<Value<'_>>;
641    fn keys(&self) -> Vec<Cow<'_, str>>;
642    fn len(&self) -> usize;
643}
644
645#[cfg(not(feature = "sync"))]
646impl<V, S> Object for HashMap<String, V, S>
647where
648    V: AsValue,
649    S: BuildHasher,
650{
651    #[inline]
652    fn get(&self, key: &str) -> Option<Value<'_>> {
653        self.get(key).map(|v| v.as_value())
654    }
655
656    #[inline]
657    fn keys(&self) -> Vec<Cow<'_, str>> {
658        self.keys().map(|s| Cow::Borrowed(s.as_str())).collect()
659    }
660
661    #[inline]
662    fn len(&self) -> usize {
663        self.len()
664    }
665}
666#[cfg(feature = "sync")]
667impl<V, S> Object for HashMap<String, V, S>
668where
669    V: AsValue,
670    S: BuildHasher + Send + Sync,
671{
672    #[inline]
673    fn get(&self, key: &str) -> Option<Value<'_>> {
674        self.get(key).map(|v| v.as_value())
675    }
676
677    #[inline]
678    fn keys(&self) -> Vec<Cow<'_, str>> {
679        self.keys().map(|s| Cow::Borrowed(s.as_str())).collect()
680    }
681
682    #[inline]
683    fn len(&self) -> usize {
684        self.len()
685    }
686}
687
688impl<O: Object> AsValue for O {
689    #[inline]
690    fn as_value(&self) -> Value<'_> {
691        Value::Object(self)
692    }
693}