arithmetic_eval/values/
object.rs

1//! `Object` and tightly related types.
2
3use core::{
4    fmt,
5    iter::{self, FromIterator},
6    ops,
7};
8
9use crate::{
10    alloc::{hash_map, HashMap, String},
11    Value,
12};
13
14/// Object with zero or more named fields.
15///
16/// An object functions similarly to a `HashMap` with [`String`] keys and [`Value`]
17/// values. It allows iteration over name-value pairs, random access by field name,
18/// inserting / removing fields etc.
19///
20/// # Examples
21///
22/// ```
23/// # use arithmetic_eval::{fns, Object, Value, ValueType};
24/// let mut obj = Object::<u32>::default();
25/// obj.insert("field", Value::Prim(0));
26/// obj.insert("other_field", Value::Bool(false));
27/// assert_eq!(obj.len(), 2);
28/// assert_eq!(obj["field"].value_type(), ValueType::Prim);
29/// assert!(obj.iter().all(|(_, val)| !val.is_void()));
30///
31/// // `Object` implements `FromIterator` / `Extend`.
32/// let fields = vec![
33///     ("third", Value::Prim(3)),
34///     ("fourth", Value::native_fn(fns::Assert)),
35/// ];
36/// let mut other_obj: Object<u32> = fields.into_iter().collect();
37/// other_obj.extend(obj);
38/// assert_eq!(other_obj.len(), 4);
39/// ```
40#[derive(Debug, Clone, PartialEq)]
41pub struct Object<T> {
42    fields: HashMap<String, Value<T>>,
43}
44
45impl<T> From<Object<T>> for Value<T> {
46    fn from(object: Object<T>) -> Self {
47        Self::Object(object)
48    }
49}
50
51impl<T> Default for Object<T> {
52    fn default() -> Self {
53        Self {
54            fields: HashMap::new(),
55        }
56    }
57}
58
59impl<T: fmt::Display> fmt::Display for Object<T> {
60    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
61        formatter.write_str("#{ ")?;
62        for (i, (name, value)) in self.iter().enumerate() {
63            write!(formatter, "{name}: {value}")?;
64            if i + 1 < self.len() {
65                write!(formatter, ", ")?;
66            } else {
67                write!(formatter, " ")?;
68            }
69        }
70        formatter.write_str("}")
71    }
72}
73
74impl<T> Object<T> {
75    /// Creates an object with a single field.
76    pub fn just(field_name: impl Into<String>, value: Value<T>) -> Self {
77        let fields = iter::once((field_name.into(), value)).collect();
78        Self { fields }
79    }
80
81    /// Returns the number of fields in this object.
82    pub fn len(&self) -> usize {
83        self.fields.len()
84    }
85
86    /// Checks if this object is empty (has no fields).
87    pub fn is_empty(&self) -> bool {
88        self.fields.is_empty()
89    }
90
91    /// Iterates over name-value pairs for all fields defined in this object.
92    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value<T>)> + '_ {
93        self.fields
94            .iter()
95            .map(|(name, value)| (name.as_str(), value))
96    }
97
98    /// Iterates over field names.
99    pub fn field_names(&self) -> impl Iterator<Item = &str> + '_ {
100        self.fields.keys().map(String::as_str)
101    }
102
103    /// Returns the value of a field with the specified name, or `None` if this object
104    /// does not contain such a field.
105    pub fn get(&self, field_name: &str) -> Option<&Value<T>> {
106        self.fields.get(field_name)
107    }
108
109    /// Checks whether this object has a field with the specified name.
110    pub fn contains_field(&self, field_name: &str) -> bool {
111        self.fields.contains_key(field_name)
112    }
113
114    /// Inserts a field into this object.
115    pub fn insert(&mut self, field_name: impl Into<String>, value: Value<T>) -> Option<Value<T>> {
116        self.fields.insert(field_name.into(), value)
117    }
118
119    /// Removes and returns the specified field from this object.
120    pub fn remove(&mut self, field_name: &str) -> Option<Value<T>> {
121        self.fields.remove(field_name)
122    }
123}
124
125impl<T> ops::Index<&str> for Object<T> {
126    type Output = Value<T>;
127
128    fn index(&self, index: &str) -> &Self::Output {
129        &self.fields[index]
130    }
131}
132
133impl<T> IntoIterator for Object<T> {
134    type Item = (String, Value<T>);
135    /// Iterator type should be considered an implementation detail.
136    type IntoIter = hash_map::IntoIter<String, Value<T>>;
137
138    fn into_iter(self) -> Self::IntoIter {
139        self.fields.into_iter()
140    }
141}
142
143impl<'r, T> IntoIterator for &'r Object<T> {
144    type Item = (&'r str, &'r Value<T>);
145    /// Iterator type should be considered an implementation detail.
146    type IntoIter = Iter<'r, T>;
147
148    fn into_iter(self) -> Self::IntoIter {
149        self.fields
150            .iter()
151            .map(|(name, value)| (name.as_str(), value))
152    }
153}
154
155pub type Iter<'r, T> = iter::Map<
156    hash_map::Iter<'r, String, Value<T>>,
157    fn((&'r String, &'r Value<T>)) -> (&'r str, &'r Value<T>),
158>;
159
160impl<T, S, V> FromIterator<(S, V)> for Object<T>
161where
162    S: Into<String>,
163    V: Into<Value<T>>,
164{
165    fn from_iter<I: IntoIterator<Item = (S, V)>>(iter: I) -> Self {
166        Self {
167            fields: iter
168                .into_iter()
169                .map(|(name, value)| (name.into(), value.into()))
170                .collect(),
171        }
172    }
173}
174
175impl<T, S, V> Extend<(S, V)> for Object<T>
176where
177    S: Into<String>,
178    V: Into<Value<T>>,
179{
180    fn extend<I: IntoIterator<Item = (S, V)>>(&mut self, iter: I) {
181        let new_fields = iter
182            .into_iter()
183            .map(|(name, value)| (name.into(), value.into()));
184        self.fields.extend(new_fields);
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191
192    #[test]
193    fn object_to_string() {
194        let mut obj = Object::<f32>::default();
195        let obj_string = obj.to_string();
196        assert_eq!(obj_string, "#{ }");
197
198        obj.insert("x", Value::Prim(3.0));
199        let obj_string = obj.to_string();
200        assert_eq!(obj_string, "#{ x: 3 }");
201
202        obj.insert("y", Value::Prim(4.0));
203        let obj_string = obj.to_string();
204        assert!(
205            obj_string == "#{ x: 3, y: 4 }" || obj_string == "#{ y: 4, x: 3 }",
206            "Unexpected obj_string: {obj_string}"
207        );
208    }
209}