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}