platform_value/
index.rs

1use core::fmt::{self, Display};
2use core::ops;
3
4use super::Value;
5use crate::value_map::{ValueMap, ValueMapHelper};
6
7/// A type that can be used to index into a `platform_value::Value`.
8///
9/// The [`get`] and [`get_mut`] methods of `Value` accept any type that
10/// implements `Index`, as does the [square-bracket indexing operator]. This
11/// trait is implemented for strings which are used as the index into a JSON
12/// map, and for `usize` which is used as the index into a JSON array.
13///
14/// [`get`]: ../enum.Value.html#method.get
15/// [`get_mut`]: ../enum.Value.html#method.get_mut
16/// [square-bracket indexing operator]: ../enum.Value.html#impl-Index%3CI%3E
17///
18/// This trait is sealed and cannot be implemented for types outside of
19/// `platform_value`.
20///
21/// # Examples
22///
23/// ```
24/// # use platform_value::platform_value;
25/// #
26/// let data = platform_value!({ "inner": [1, 2, 3] });
27///
28/// // Data is a JSON map so it can be indexed with a string.
29/// let inner = &data["inner"];
30///
31/// // Inner is a JSON array so it can be indexed with an integer.
32/// let first = &inner[0];
33///
34/// assert_eq!(first, 1);
35/// ```
36pub trait Index: private::Sealed {
37    /// Return None if the key is not already in the array or object.
38    #[doc(hidden)]
39    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value>;
40
41    /// Return None if the key is not already in the array or object.
42    #[doc(hidden)]
43    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value>;
44
45    /// Panic if array index out of bounds. If key is not already in the object,
46    /// insert it with a value of null. Panic if Value is a type that cannot be
47    /// indexed into, except if Value is null then it can be treated as an empty
48    /// object.
49    #[doc(hidden)]
50    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value;
51}
52
53impl Index for usize {
54    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
55        match v {
56            Value::Array(vec) => vec.get(*self),
57            _ => None,
58        }
59    }
60    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
61        match v {
62            Value::Array(vec) => vec.get_mut(*self),
63            _ => None,
64        }
65    }
66    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
67        match v {
68            Value::Array(vec) => {
69                let len = vec.len();
70                vec.get_mut(*self).unwrap_or_else(|| {
71                    panic!(
72                        "cannot access index {} of JSON array of length {}",
73                        self, len
74                    )
75                })
76            }
77            _ => panic!("cannot access index {} of JSON {}", self, Type(v)),
78        }
79    }
80}
81
82impl Index for str {
83    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
84        match v {
85            Value::Map(map) => map.get_optional_key(self),
86            _ => None,
87        }
88    }
89    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
90        match v {
91            Value::Map(map) => map.get_optional_key_mut(self),
92            _ => None,
93        }
94    }
95    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
96        if let Value::Null = v {
97            *v = Value::Map(ValueMap::new());
98        }
99        match v {
100            Value::Map(map) => map.get_key_mut_or_insert(self, Value::Null),
101            _ => panic!("cannot access key {:?} in JSON {}", self, Type(v)),
102        }
103    }
104}
105
106impl Index for String {
107    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
108        self[..].index_into(v)
109    }
110    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
111        self[..].index_into_mut(v)
112    }
113    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
114        self[..].index_or_insert(v)
115    }
116}
117
118impl<T> Index for &T
119where
120    T: ?Sized + Index,
121{
122    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
123        (**self).index_into(v)
124    }
125    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
126        (**self).index_into_mut(v)
127    }
128    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
129        (**self).index_or_insert(v)
130    }
131}
132
133// Prevent users from implementing the Index trait.
134mod private {
135    pub trait Sealed {}
136    impl Sealed for usize {}
137    impl Sealed for str {}
138    impl Sealed for String {}
139    impl<T> Sealed for &T where T: ?Sized + Sealed {}
140}
141
142/// Used in panic messages.
143struct Type<'a>(&'a Value);
144
145impl Display for Type<'_> {
146    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
147        match *self.0 {
148            Value::Null => formatter.write_str("null"),
149            Value::Bool(_) => formatter.write_str("boolean"),
150            Value::Float(_) => formatter.write_str("float"),
151            Value::Text(_) => formatter.write_str("string"),
152            Value::Array(_) => formatter.write_str("array"),
153            Value::Map(_) => formatter.write_str("map"),
154            Value::U128(_) => formatter.write_str("u128"),
155            Value::I128(_) => formatter.write_str("i128"),
156            Value::U64(_) => formatter.write_str("u64"),
157            Value::I64(_) => formatter.write_str("i64"),
158            Value::U32(_) => formatter.write_str("u32"),
159            Value::I32(_) => formatter.write_str("i32"),
160            Value::U16(_) => formatter.write_str("u16"),
161            Value::I16(_) => formatter.write_str("i16"),
162            Value::U8(_) => formatter.write_str("u8"),
163            Value::I8(_) => formatter.write_str("i8"),
164            Value::Bytes(_) => formatter.write_str("bytes"),
165            Value::Bytes20(_) => formatter.write_str("bytes20"),
166            Value::Bytes32(_) => formatter.write_str("bytes32"),
167            Value::Bytes36(_) => formatter.write_str("bytes36"),
168            Value::Identifier(_) => formatter.write_str("identifier"),
169            Value::EnumU8(_) => formatter.write_str("enum u8"),
170            Value::EnumString(_) => formatter.write_str("enum string"),
171        }
172    }
173}
174
175// The usual semantics of Index is to panic on invalid indexing.
176//
177// That said, the usual semantics are for things like Vec and BTreeMap which
178// have different use cases than Value. If you are working with a Vec, you know
179// that you are working with a Vec and you can get the len of the Vec and make
180// sure your indices are within bounds. The Value use cases are more
181// loosey-goosey. You got some JSON from an endpoint and you want to pull values
182// out of it. Outside of this Index impl, you already have the option of using
183// value.as_array() and working with the Vec directly, or matching on
184// Value::Array and getting the Vec directly. The Index impl means you can skip
185// that and index directly into the thing using a concise syntax. You don't have
186// to check the type, you don't have to check the len, it is all about what you
187// expect the Value to look like.
188//
189// Basically the use cases that would be well served by panicking here are
190// better served by using one of the other approaches: get and get_mut,
191// as_array, or match. The value of this impl is that it adds a way of working
192// with Value that is not well served by the existing approaches: concise and
193// careless and sometimes that is exactly what you want.
194impl<I> ops::Index<I> for Value
195where
196    I: Index,
197{
198    type Output = Value;
199
200    /// Index into a `serde_json::Value` using the syntax `value[0]` or
201    /// `value["k"]`.
202    ///
203    /// Returns `Value::Null` if the type of `self` does not match the type of
204    /// the index, for example if the index is a string and `self` is an array
205    /// or a number. Also returns `Value::Null` if the given key does not exist
206    /// in the map or the given index is not within the bounds of the array.
207    ///
208    /// For retrieving deeply nested values, you should have a look at the
209    /// `Value::pointer` method.
210    ///
211    /// # Examples
212    ///
213    /// ```
214    /// # use platform_value::platform_value;
215    /// #
216    /// let data = platform_value!({
217    ///     "x": {
218    ///         "y": ["z", "zz"]
219    ///     }
220    /// });
221    ///
222    /// assert_eq!(data["x"]["y"], platform_value!(["z", "zz"]));
223    /// assert_eq!(data["x"]["y"][0], platform_value!("z"));
224    ///
225    /// assert_eq!(data["a"], platform_value!(null)); // returns null for undefined values
226    /// assert_eq!(data["a"]["b"], platform_value!(null)); // does not panic
227    /// ```
228    fn index(&self, index: I) -> &Value {
229        static NULL: Value = Value::Null;
230        index.index_into(self).unwrap_or(&NULL)
231    }
232}
233
234impl<I> ops::IndexMut<I> for Value
235where
236    I: Index,
237{
238    /// Write into a `serde_json::Value` using the syntax `value[0] = ...` or
239    /// `value["k"] = ...`.
240    ///
241    /// If the index is a number, the value must be an array of length bigger
242    /// than the index. Indexing into a value that is not an array or an array
243    /// that is too small will panic.
244    ///
245    /// If the index is a string, the value must be an object or null which is
246    /// treated like an empty object. If the key is not already present in the
247    /// object, it will be inserted with a value of null. Indexing into a value
248    /// that is neither an object nor null will panic.
249    ///
250    /// # Examples
251    ///
252    /// ```
253    /// # use platform_value::platform_value;
254    /// #
255    /// let mut data = platform_value!({ "x": 0 });
256    ///
257    /// // replace an existing key
258    /// data["x"] = platform_value!(1);
259    ///
260    /// // insert a new key
261    /// data["y"] = platform_value!([false, false, false]);
262    ///
263    /// // replace an array value
264    /// data["y"][0] = platform_value!(true);
265    ///
266    /// // inserted a deeply nested key
267    /// data["a"]["b"]["c"]["d"] = platform_value!(true);
268    ///
269    /// println!("{}", data);
270    /// ```
271    fn index_mut(&mut self, index: I) -> &mut Value {
272        index.index_or_insert(self)
273    }
274}