Skip to main content

immutable_json/
object.rs

1use crate::api::Number::{Decimal, Integer};
2use crate::api::{Number, Value};
3use crate::array::Array;
4use imbl::hashmap::Iter;
5use imbl::shared_ptr::DefaultSharedPtr;
6use imbl::HashMap;
7use std::fmt::{Debug, Display, Formatter};
8use std::hash::{Hash, Hasher};
9
10/// Represents a JSON object.
11#[derive(Clone, Debug, Eq, PartialEq)]
12pub struct Object {
13    map: HashMap<String, Value>,
14}
15
16impl Default for Object {
17    fn default() -> Self {
18        Self::new()
19    }
20}
21
22impl Display for Object {
23    /// Converts a JSON object to a string.
24    /// ```rust
25    /// # use immutable_json::api::Value;
26    /// # use immutable_json::error::Error;
27    /// # use std::str::FromStr;
28    /// # fn main() -> Result<(), Error>{
29    ///let data = r#"
30    ///    {
31    ///        "string": "string",
32    ///        "int": 43,
33    ///        "float": 5.8,
34    ///        "boolean": true,
35    ///        "object": {"test": "test"},
36    ///        "array": [
37    ///            "string",
38    ///            1,
39    ///            3.0,
40    ///            false,
41    ///            {"test": "test"},
42    ///            [1]
43    ///        ]
44    ///    }"#;
45    ///
46    ///let v: serde_json::Value = serde_json::from_str(data)?;
47    ///
48    ///assert_eq!(Some(v), serde_json::from_str(&Value::from_str(data)?.to_string()).ok());
49    /// #   Ok(())
50    /// # }
51    /// ```
52    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
53        Display::fmt(&Value::Object(self.clone()), f)
54    }
55}
56
57impl FromIterator<(String, Value)> for Object {
58    /// Iterates over the key/value pairs of a JSON object.
59    fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
60        iter.into_iter().fold(Self::new(), |o, (k, v)| o.add(&k, &v))
61    }
62}
63
64impl Hash for Object {
65    fn hash<H: Hasher>(&self, state: &mut H) {
66        self.map.iter().for_each(|(k, v)| {
67            k.hash(state);
68            v.hash(state)
69        })
70    }
71}
72
73impl<'a> IntoIterator for &'a Object {
74    type Item = (String, Value);
75    type IntoIter = ObjectIter<'a>;
76
77    fn into_iter(self) -> ObjectIter<'a> {
78        self.iter()
79    }
80}
81
82impl Object {
83    /// Adds a field to an object.
84    /// ```rust
85    /// # use immutable_json::api::Value::String;
86    /// # use immutable_json::object::Object;
87    /// # fn main() {
88    /// assert_eq!(Some("test".to_string()),
89    ///     Object::new().add("test", &String("test".to_string())).get_string("test"));
90    /// # }
91    /// ```
92    pub fn add(&self, key: &str, value: &Value) -> Self {
93        let mut new_map = self.map.clone();
94
95        new_map.insert(key.to_string(), value.clone());
96        Self { map: new_map }
97    }
98
99    /// Adds a field to an object as an array.
100    /// ```rust
101    /// # use immutable_json::array::Array;
102    /// # use immutable_json::object::Object;
103    /// # fn main() {
104    /// assert_eq!(Some(1),
105    ///     Object::new()
106    ///         .add_array("test", &Array::new().add_integer(1))
107    ///         .get_array("test").and_then(|a| a.get_integer(0).ok()?));
108    /// # }
109    /// ```
110    pub fn add_array(&self, key: &str, value: &Array) -> Self {
111        self.add(key, &Value::Array(value.clone()))
112    }
113
114    /// Adds a field to an object as a bool.
115    /// ```rust
116    /// # use immutable_json::object::Object;
117    /// # fn main() {
118    /// assert_eq!(Some(true), Object::new().add_bool("test", true).get_bool("test"));
119    /// # }
120    /// ```
121    pub fn add_bool(&self, key: &str, value: bool) -> Self {
122        self.add(key, &Value::Bool(value))
123    }
124
125    /// Adds a field to an object as a decimal.
126    /// ```rust
127    /// # use immutable_json::object::Object;
128    /// # fn main() {
129    /// assert_eq!(Some(3.0), Object::new().add_decimal("test", 3.0).get_decimal("test"));
130    /// # }
131    /// ```
132    pub fn add_decimal(&self, key: &str, value: f64) -> Self {
133        self.add(key, &Value::Number(Decimal(value)))
134    }
135
136    /// Adds a field to an object as an integer.
137    /// ```rust
138    /// # use immutable_json::object::Object;
139    /// # fn main() {
140    /// assert_eq!(Some(3), Object::new().add_integer("test", 3).get_integer("test"));
141    /// # }
142    /// ```
143    pub fn add_integer(&self, key: &str, value: i128) -> Self {
144        self.add(key, &Value::Number(Integer(value)))
145    }
146
147    /// Adds a field to an object as a number.
148    /// ```rust
149    /// # use immutable_json::object::Object;
150    /// # use immutable_json::api::Number::Integer;
151    /// # fn main() {
152    /// assert_eq!(Some(3),
153    ///     Object::new()
154    ///         .add_number("test", Integer(3))
155    ///         .get_number("test").and_then(|n| n.as_integer()));
156    /// # }
157    /// ```
158    pub fn add_number(&self, key: &str, value: Number) -> Self {
159        self.add(key, &Value::Number(value))
160    }
161
162    /// Adds a field to an object as an object.
163    /// ```rust
164    /// # use immutable_json::object::Object;
165    /// # use immutable_json::api::Number::Integer;
166    /// # fn main() {
167    /// assert_eq!(Some(1),
168    ///     Object::new()
169    ///         .add_object("test", &Object::new().add_integer("test", 1))
170    ///         .get_object("test").and_then(|o| o.get_integer("test")));
171    /// # }
172    /// ```
173    pub fn add_object(&self, key: &str, value: &Object) -> Self {
174        self.add(key, &Value::Object(value.clone()))
175    }
176
177    /// Adds a field to an object as an integer.
178    /// ```rust
179    /// # use immutable_json::object::Object;
180    /// # fn main() {
181    /// assert_eq!(Some("test".to_string()),
182    ///     Object::new().add_string("test", "test").get_string("test"));
183    /// # }
184    /// ```
185    pub fn add_string(&self, key: &str, value: &str) -> Self {
186        self.add(key, &Value::String(value.to_string()))
187    }
188
189    /// Returns a value from the object if it exists at the given key.
190    /// ```rust
191    /// # use immutable_json::object::Object;
192    /// # fn main() {
193    /// let object = Object::new().add_string("test1", "test");
194    ///
195    /// assert_eq!(Some("test".to_string()), object.get_string("test1"));
196    /// assert_eq!(None, object.get_string("test2"));
197    /// # }
198    /// ```
199    pub fn get(&self, key: &str) -> Option<&Value> {
200        self.map.get(&key.to_string())
201    }
202
203    /// Returns a value from the object if it exists at the given key and if it is an array.
204    /// Otherwise, `None` is returned.
205    /// ```rust
206    /// # use immutable_json::array::Array;
207    /// # use immutable_json::object::Object;
208    /// # fn main() {
209    /// let object = Object::new()
210    ///     .add_array("test1", &Array::new().add_integer(3))
211    ///     .add_integer("test2", 3);
212    ///
213    /// assert_eq!(Some(3), object.get_array("test1").and_then(|a| a.get_integer(0).ok()?));
214    /// assert_eq!(None, object.get_array("test2"));
215    /// # }
216    /// ```
217    pub fn get_array(&self, key: &str) -> Option<Array> {
218        self.get(key).and_then(|v| v.as_array())
219    }
220
221    /// Returns a value from the object if it exists at the given key and if it is a bool.
222    /// Otherwise, `None` is returned.
223    /// ```rust
224    /// # use immutable_json::object::Object;
225    /// # fn main() {
226    /// let object = Object::new()
227    ///     .add_bool("test1", true)
228    ///     .add_integer("test2", 3);
229    ///
230    /// assert_eq!(Some(true), object.get_bool("test1"));
231    /// assert_eq!(None, object.get_string("test2"));
232    /// # }
233    /// ```
234    pub fn get_bool(&self, key: &str) -> Option<bool> {
235        self.get(key).and_then(|v| v.as_bool())
236    }
237
238    /// Returns a value from the object if it exists at the given key and if it is a decimal.
239    /// Otherwise, `None` is returned.
240    /// ```rust
241    /// # use immutable_json::object::Object;
242    /// # fn main() {
243    /// let object = Object::new()
244    ///     .add_decimal("test1", 3.0)
245    ///     .add_integer("test2", 3);
246    ///
247    /// assert_eq!(Some(3.0), object.get_decimal("test1"));
248    /// assert_eq!(None, object.get_string("test2"));
249    /// # }
250    /// ```
251    pub fn get_decimal(&self, key: &str) -> Option<f64> {
252        self.get(key).and_then(|v| v.as_decimal())
253    }
254
255    /// Returns a value from the object if it exists at the given key and if it is an integer.
256    /// Otherwise, `None` is returned.
257    /// ```rust
258    /// # use immutable_json::object::Object;
259    /// # fn main() {
260    /// let object = Object::new()
261    ///     .add_decimal("test1", 3.0)
262    ///     .add_integer("test2", 3);
263    ///
264    /// assert_eq!(Some(3.0), object.get_decimal("test1"));
265    /// assert_eq!(None, object.get_string("test2"));
266    /// # }
267    /// ```
268    pub fn get_integer(&self, key: &str) -> Option<i128> {
269        self.get(key).and_then(|v| v.as_integer())
270    }
271
272    /// Returns a value from the object if it exists at the given key and if it is a number.
273    /// Otherwise, `None` is returned.
274    /// ```rust
275    /// # use immutable_json::api::Number;
276    /// # use immutable_json::object::Object;
277    /// # fn main() {
278    /// let object = Object::new()
279    ///     .add_number("test1", Number::Decimal(3.0))
280    ///     .add_integer("test2", 3);
281    ///
282    /// assert_eq!(Some(3.0), object.get_number("test1").and_then(|n| n.as_decimal()));
283    /// assert_eq!(None, object.get_string("test2"));
284    /// # }
285    /// ```
286    pub fn get_number(&self, key: &str) -> Option<Number> {
287        self.get(key).and_then(|v| v.as_number())
288    }
289
290    /// Returns a value from the object if it exists at the given key and if it is an object.
291    /// Otherwise, `None` is returned.
292    /// ```rust
293    /// # use immutable_json::object::Object;
294    /// # fn main() {
295    /// let object = Object::new()
296    ///     .add_object("test1", &Object::new().add_integer("test", 3))
297    ///     .add_integer("test2", 3);
298    ///
299    /// assert_eq!(Some(3), object.get_object("test1").and_then(|a| a.get_integer("test")));
300    /// assert_eq!(None, object.get_array("test2"));
301    /// # }
302    /// ```
303    pub fn get_object(&self, key: &str) -> Option<Object> {
304        self.get(key).and_then(|v| v.as_object())
305    }
306
307    /// Returns a value from the object if it exists at the given key and if it is a string.
308    /// Otherwise, `None` is returned.
309    /// ```rust
310    /// # use immutable_json::object::Object;
311    /// # fn main() {
312    /// let object = Object::new()
313    ///     .add_string("test1", "test")
314    ///     .add_integer("test2", 3);
315    ///
316    /// assert_eq!(Some("test".to_string()), object.get_string("test1"));
317    /// assert_eq!(None, object.get_string("test2"));
318    /// # }
319    /// ```
320    pub fn get_string(&self, key: &str) -> Option<String> {
321        self.get(key).and_then(|v| v.as_string())
322    }
323
324    /// Returns `true` if the object has a field with the given key.
325    /// ```rust
326    /// # use immutable_json::object::Object;
327    /// # fn main() {
328    /// let object = Object::new().add_string("test1", "test");
329    ///
330    /// assert_eq!(true, object.has_key("test1"));
331    /// assert_eq!(false, object.has_key("test2"));
332    /// # }
333    /// ```
334    pub fn has_key(&self, key: &str) -> bool {
335        self.map.contains_key(&key.to_string())
336    }
337
338    /// Returns an iterator over the key/value tuples of the object.
339    /// ```rust
340    /// # use immutable_json::object::Object;
341    /// # fn main() {
342    /// let object = Object::new().add_string("test1", "test1").add_integer("test2", 1);
343    ///
344    /// assert_eq!(object, Object::from_iter(object.iter()));
345    /// # }
346    /// ```
347    pub fn iter(&'_ self) -> ObjectIter<'_> {
348        ObjectIter {
349            iter: self.map.iter(),
350        }
351    }
352
353    /// Create an empty JSON object.
354    pub fn new() -> Self {
355        Self {
356            map: HashMap::new(),
357        }
358    }
359
360    /// Removes the field with the given key from the object.
361    /// ```rust
362    /// # use immutable_json::object::Object;
363    /// # fn main() {
364    /// let object = Object::new().add_string("test", "test");
365    ///
366    /// assert_eq!(true, object.has_key("test"));
367    /// assert_eq!(false, object.remove("test").has_key("test"));
368    /// # }
369    /// ```
370    pub fn remove(&self, key: &str) -> Self {
371        let mut new_map = self.map.clone();
372
373        new_map.remove(&key.to_string());
374        Self { map: new_map }
375    }
376}
377
378#[derive(Clone)]
379pub struct ObjectIter<'a> {
380    iter: Iter<'a, String, Value, DefaultSharedPtr>,
381}
382
383impl<'a> Iterator for ObjectIter<'a> {
384    type Item = (String, Value);
385
386    fn next(&mut self) -> Option<Self::Item> {
387        self.iter.next().map(|i| (i.0.clone(), i.1.clone()))
388    }
389}