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}