mason_rs/
index.rs

1use std::{collections::HashMap, ops};
2
3use crate::Value;
4
5/// A type that can be used to index into a `mason_rs::Value`.
6///
7/// The [`get`] and [`get_mut`] methods of `Value` accept any type that
8/// implements `Index`, as does the [square-bracket indexing operator]. This
9/// trait is implemented for strings which are used as the index into a MASON
10/// object, and for `usize` which is used as the index into a MASON array.
11///
12/// [`get`]: Value::get
13/// [`get_mut`]: Value::get_mut
14/// [square-bracket indexing operator]: Value#impl-Index%3CI%3E-for-Value
15///
16/// This trait is sealed and cannot be implemented for types outside of
17/// `mason`.
18///
19/// # Examples
20///
21/// ```
22/// # use mason_rs::Value;
23/// # use std::str::FromStr;
24/// #
25/// let data = Value::from_str(r#"{ "inner": [1, 2, 3] }"#).unwrap();
26///
27/// // Data is a MASON object so it can be indexed with a string.
28/// let inner = &data["inner"];
29///
30/// // Inner is a MASON array so it can be indexed with an integer.
31/// let first = &inner[0];
32///
33/// assert_eq!(*first, Value::Number(1.0));
34/// ```
35pub trait Index: private::Sealed {
36    #[doc(hidden)]
37    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value>;
38
39    #[doc(hidden)]
40    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value>;
41
42    #[doc(hidden)]
43    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value;
44}
45
46impl Index for usize {
47    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
48        match v {
49            Value::Array(vec) => vec.get(*self),
50            _ => None,
51        }
52    }
53    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
54        match v {
55            Value::Array(vec) => vec.get_mut(*self),
56            _ => None,
57        }
58    }
59
60    /// Panics if index is bigger than array length. If index is equal to length,
61    /// a Value of Null is inserted and returned.
62    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
63        match v {
64            Value::Array(vec) => {
65                let mut len = vec.len();
66
67                // Insert default value to make it possible to insert elements
68                // using indexing like you can with objects.
69                if *self == len {
70                    vec.push(Value::Null);
71                    len += 1;
72                }
73
74                vec.get_mut(*self).unwrap_or_else(|| {
75                    panic!("cannot access index {self} of MASON array of length {len}")
76                })
77            }
78            _ => panic!("cannot access index {} of MASON {}", self, v.value_type()),
79        }
80    }
81}
82
83impl Index for str {
84    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
85        match v {
86            Value::Object(map) => map.get(self),
87            _ => None,
88        }
89    }
90    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
91        match v {
92            Value::Object(map) => map.get_mut(self),
93            _ => None,
94        }
95    }
96
97    /// Panics if Value is neither an Object or Null. If Value is Null,
98    /// it will be treated as an empty object. If the key is not already
99    /// in the object, insert it with a value of null.
100    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
101        if matches!(v, Value::Null) {
102            *v = Value::Object(HashMap::new());
103        }
104        match v {
105            Value::Object(map) => map.entry(self.to_owned()).or_insert(Value::Null),
106            _ => panic!("cannot access key {:?} in MASON {}", self, v.value_type()),
107        }
108    }
109}
110
111impl Index for String {
112    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
113        self[..].index_into(v)
114    }
115    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
116        self[..].index_into_mut(v)
117    }
118    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
119        self[..].index_or_insert(v)
120    }
121}
122
123impl<T> Index for &T
124where
125    T: ?Sized + Index,
126{
127    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
128        (**self).index_into(v)
129    }
130    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
131        (**self).index_into_mut(v)
132    }
133    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
134        (**self).index_or_insert(v)
135    }
136}
137
138// The usual semantics of Index is to panic on invalid indexing, but this
139// does not make much sense for indexing into a Value. For this reason,
140// invalid indexing returns `Value::Null`.
141impl<I> ops::Index<I> for Value
142where
143    I: Index,
144{
145    type Output = Self;
146
147    /// Index into a `mason_rs::Value` using the syntax `value[0]` or
148    /// `value["k"]`.
149    ///
150    /// Returns `Value::Null` if the type of `self` does not match the type of
151    /// the index, or if the given key does not exist in the map or the given
152    /// index is not within the bounds of the array.
153    ///
154    /// # Examples
155    ///
156    /// ```
157    /// # use mason_rs::Value;
158    /// # use std::str::FromStr;
159    /// #
160    ///
161    /// let data = Value::from_str(r#"{
162    ///     "x": {
163    ///         "y": ["z", "zz"]
164    ///     }
165    /// }"#).unwrap();
166    ///
167    /// assert_eq!(
168    ///     data["x"]["y"],
169    ///     Value::Array(vec![
170    ///         Value::String("z".into()),
171    ///         Value::String("zz".into()),
172    ///     ]),
173    /// );
174    /// assert_eq!(data["x"]["y"][0], Value::String("z".into()));
175    ///
176    /// assert_eq!(data["a"], Value::Null); // returns null for undefined values
177    /// assert_eq!(data["a"]["b"], Value::Null); // does not panic
178    /// ```
179    fn index(&self, index: I) -> &Self {
180        static NULL: Value = Value::Null;
181        index.index_into(self).unwrap_or(&NULL)
182    }
183}
184
185impl<I> ops::IndexMut<I> for Value
186where
187    I: Index,
188{
189    /// Write into a `mason_rs::Value` using the syntax `value[0] = ...` or
190    /// `value["k"] = ...`.
191    ///
192    /// If the index is a number, the value must be an array of length bigger
193    /// than or equal to the index. Indexing into a value that is not an array or an array
194    /// that is too small will panic.
195    ///
196    /// If the index is a string, the value must be an object or null which is
197    /// treated like an empty object. If the key is not already present in the
198    /// object, it will be inserted with a value of null. Indexing into a value
199    /// that is neither an object nor null will panic.
200    ///
201    /// # Examples
202    ///
203    /// ```
204    /// # use mason_rs::Value;
205    /// # use std::str::FromStr;
206    /// #
207    /// let mut data = Value::from_str(r#"{ "x": 0 }"#).unwrap();
208    ///
209    /// // replace an existing key
210    /// data["x"] = Value::Number(1.0);
211    ///
212    /// // insert a new key
213    /// data["y"] = Value::Array(vec![Value::Bool(false), Value::Bool(true)]);
214    ///
215    /// // replace an array value
216    /// data["y"][0] = Value::Bool(true);
217    ///
218    /// // insert a new array value
219    /// data["y"][2] = Value::Number(1.3);
220    ///
221    /// // inserted a deeply nested key
222    /// data["a"]["b"]["c"]["d"] = Value::Bool(true);
223    ///
224    /// println!("{:?}", data);
225    /// ```
226    fn index_mut(&mut self, index: I) -> &mut Self {
227        index.index_or_insert(self)
228    }
229}
230
231// Prevent users from implementing the Index trait.
232mod private {
233    pub trait Sealed {}
234    impl Sealed for usize {}
235    impl Sealed for str {}
236    impl Sealed for String {}
237    impl<T> Sealed for &T where T: ?Sized + Sealed {}
238}