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}
275
276#[cfg(test)]
277mod tests {
278    use super::*;
279    use crate::platform_value;
280
281    // ===============================================================
282    // Index<usize> for Value — access array element
283    // ===============================================================
284
285    #[test]
286    fn index_usize_access_array_element() {
287        let value = platform_value!([10, 20, 30]);
288        assert_eq!(value[0], platform_value!(10));
289        assert_eq!(value[1], platform_value!(20));
290        assert_eq!(value[2], platform_value!(30));
291    }
292
293    // ===============================================================
294    // Index<usize> for Value — out-of-bounds returns Null
295    // ===============================================================
296
297    #[test]
298    fn index_usize_out_of_bounds_returns_null() {
299        let value = platform_value!([10, 20]);
300        // The ops::Index impl returns &NULL for missing indices
301        assert_eq!(value[99], Value::Null);
302    }
303
304    // ===============================================================
305    // Index<usize> for Value — non-array returns Null
306    // ===============================================================
307
308    #[test]
309    fn index_usize_on_non_array_returns_null() {
310        let value = platform_value!(42);
311        // ops::Index returns &NULL when index_into returns None
312        assert_eq!(value[0], Value::Null);
313    }
314
315    #[test]
316    fn index_usize_on_map_returns_null() {
317        let value = platform_value!({ "key": "val" });
318        assert_eq!(value[0], Value::Null);
319    }
320
321    // ===============================================================
322    // IndexMut<usize> — panic on out-of-bounds
323    // ===============================================================
324
325    #[test]
326    #[should_panic(expected = "cannot access index 5 of JSON array of length 2")]
327    fn index_mut_usize_out_of_bounds_panics() {
328        let mut value = platform_value!([10, 20]);
329        value[5] = platform_value!(99);
330    }
331
332    // ===============================================================
333    // IndexMut<usize> — panic on non-array
334    // ===============================================================
335
336    #[test]
337    #[should_panic(expected = "cannot access index 0 of JSON")]
338    fn index_mut_usize_on_non_array_panics() {
339        let mut value = platform_value!(42);
340        value[0] = platform_value!(99);
341    }
342
343    // ===============================================================
344    // IndexMut<usize> — successfully write
345    // ===============================================================
346
347    #[test]
348    fn index_mut_usize_write() {
349        let mut value = platform_value!([10, 20, 30]);
350        value[1] = platform_value!(99);
351        assert_eq!(value[1], platform_value!(99));
352    }
353
354    // ===============================================================
355    // Index<&str> for Value — access map key
356    // ===============================================================
357
358    #[test]
359    fn index_str_access_map_key() {
360        let value = platform_value!({ "name": "Alice", "age": 30 });
361        assert_eq!(value["name"], platform_value!("Alice"));
362        assert_eq!(value["age"], platform_value!(30));
363    }
364
365    // ===============================================================
366    // Index<&str> for Value — missing key returns Null
367    // ===============================================================
368
369    #[test]
370    fn index_str_missing_key_returns_null() {
371        let value = platform_value!({ "name": "Alice" });
372        assert_eq!(value["missing"], Value::Null);
373    }
374
375    // ===============================================================
376    // Index<&str> for Value — non-map returns Null
377    // ===============================================================
378
379    #[test]
380    fn index_str_on_non_map_returns_null() {
381        let value = platform_value!(42);
382        assert_eq!(value["key"], Value::Null);
383    }
384
385    #[test]
386    fn index_str_on_array_returns_null() {
387        let value = platform_value!([1, 2, 3]);
388        assert_eq!(value["key"], Value::Null);
389    }
390
391    // ===============================================================
392    // Index<&str> for Value — nested access
393    // ===============================================================
394
395    #[test]
396    fn index_str_nested_access() {
397        let value = platform_value!({
398            "outer": {
399                "inner": {
400                    "deep": 42
401                }
402            }
403        });
404        assert_eq!(value["outer"]["inner"]["deep"], platform_value!(42));
405    }
406
407    #[test]
408    fn index_str_nested_missing_returns_null_chain() {
409        let value = platform_value!({ "a": { "b": 1 } });
410        // "a" -> "c" -> doesn't exist, returns Null
411        // then Null["anything"] also returns Null
412        assert_eq!(value["a"]["c"], Value::Null);
413        assert_eq!(value["a"]["c"]["d"], Value::Null);
414    }
415
416    // ===============================================================
417    // IndexMut<&str> — write to existing key
418    // ===============================================================
419
420    #[test]
421    fn index_mut_str_write_existing() {
422        let mut value = platform_value!({ "x": 0 });
423        value["x"] = platform_value!(42);
424        assert_eq!(value["x"], platform_value!(42));
425    }
426
427    // ===============================================================
428    // IndexMut<&str> — insert new key
429    // ===============================================================
430
431    #[test]
432    fn index_mut_str_insert_new_key() {
433        let mut value = platform_value!({ "x": 0 });
434        value["y"] = platform_value!("hello");
435        assert_eq!(value["y"], platform_value!("hello"));
436    }
437
438    // ===============================================================
439    // IndexMut<&str> — Null becomes empty map
440    // ===============================================================
441
442    #[test]
443    fn index_mut_str_null_becomes_map() {
444        let mut value = Value::Null;
445        value["key"] = platform_value!(1);
446        assert_eq!(value["key"], platform_value!(1));
447        assert!(value.is_map());
448    }
449
450    // ===============================================================
451    // IndexMut<&str> — deeply nested insert via Null
452    // ===============================================================
453
454    #[test]
455    fn index_mut_str_deeply_nested_insert() {
456        let mut value = platform_value!({ "x": 0 });
457        // "a" -> inserts Null, then Null becomes map for "b", etc.
458        value["a"]["b"]["c"] = platform_value!(true);
459        assert_eq!(value["a"]["b"]["c"], platform_value!(true));
460    }
461
462    // ===============================================================
463    // IndexMut<&str> — panic on non-map non-null
464    // ===============================================================
465
466    #[test]
467    #[should_panic(expected = "cannot access key")]
468    fn index_mut_str_on_non_map_panics() {
469        let mut value = platform_value!(42);
470        value["key"] = platform_value!(1);
471    }
472
473    // ===============================================================
474    // Index<String> delegates to str
475    // ===============================================================
476
477    #[test]
478    fn index_string_delegates_to_str() {
479        let value = platform_value!({ "name": "Bob" });
480        let key = String::from("name");
481        assert_eq!(value[&key], platform_value!("Bob"));
482    }
483
484    // ===============================================================
485    // IndexMut<String> delegates to str
486    // ===============================================================
487
488    #[test]
489    fn index_mut_string_delegates_to_str() {
490        let mut value = platform_value!({ "name": "Bob" });
491        let key = String::from("name");
492        value[&key] = platform_value!("Alice");
493        assert_eq!(value["name"], platform_value!("Alice"));
494    }
495
496    // ===============================================================
497    // index_into — returns None for various non-matching types
498    // ===============================================================
499
500    #[test]
501    fn index_into_usize_returns_none_for_non_array() {
502        let value = Value::Text("hello".into());
503        assert!(0usize.index_into(&value).is_none());
504    }
505
506    #[test]
507    fn index_into_str_returns_none_for_non_map() {
508        let value = Value::Array(vec![Value::U32(1)]);
509        assert!("key".index_into(&value).is_none());
510    }
511
512    // ===============================================================
513    // index_into_mut — returns None for non-matching types
514    // ===============================================================
515
516    #[test]
517    fn index_into_mut_usize_returns_none_for_non_array() {
518        let mut value = Value::Bool(true);
519        assert!(0usize.index_into_mut(&mut value).is_none());
520    }
521
522    #[test]
523    fn index_into_mut_str_returns_none_for_non_map() {
524        let mut value = Value::U64(100);
525        assert!("key".index_into_mut(&mut value).is_none());
526    }
527
528    // ===============================================================
529    // index_into_mut — returns Some for valid accesses
530    // ===============================================================
531
532    #[test]
533    fn index_into_mut_usize_returns_some() {
534        let mut value = platform_value!([10, 20]);
535        let got = 0usize.index_into_mut(&mut value);
536        assert!(got.is_some());
537        *got.unwrap() = platform_value!(99);
538        assert_eq!(value[0], platform_value!(99));
539    }
540
541    #[test]
542    fn index_into_mut_str_returns_some() {
543        let mut value = platform_value!({ "k": 1 });
544        let got = "k".index_into_mut(&mut value);
545        assert!(got.is_some());
546        *got.unwrap() = platform_value!(42);
547        assert_eq!(value["k"], platform_value!(42));
548    }
549
550    // ===============================================================
551    // Combined array + map indexing
552    // ===============================================================
553
554    #[test]
555    fn combined_array_map_indexing() {
556        let value = platform_value!({
557            "items": [
558                { "name": "first" },
559                { "name": "second" }
560            ]
561        });
562        assert_eq!(value["items"][0]["name"], platform_value!("first"));
563        assert_eq!(value["items"][1]["name"], platform_value!("second"));
564    }
565
566    #[test]
567    fn combined_array_map_indexing_mut() {
568        let mut value = platform_value!({
569            "items": [
570                { "name": "first" },
571                { "name": "second" }
572            ]
573        });
574        value["items"][0]["name"] = platform_value!("updated");
575        assert_eq!(value["items"][0]["name"], platform_value!("updated"));
576    }
577
578    // ===============================================================
579    // get() method — returns Some for existing, None for missing
580    // ===============================================================
581
582    #[test]
583    fn get_method_returns_some_for_existing_key() {
584        let value = platform_value!({ "x": 10 });
585        let result = value.get("x").unwrap();
586        assert!(result.is_some());
587        assert_eq!(result.unwrap(), &platform_value!(10));
588    }
589
590    #[test]
591    fn get_method_returns_none_for_missing_key() {
592        let value = platform_value!({ "x": 10 });
593        let result = value.get("y").unwrap();
594        assert!(result.is_none());
595    }
596
597    #[test]
598    fn get_method_errors_on_non_map() {
599        let value = platform_value!(42);
600        let result = value.get("key");
601        assert!(result.is_err());
602    }
603
604    // ===============================================================
605    // Type display coverage (used in panic messages)
606    // ===============================================================
607
608    #[test]
609    fn type_display_covers_all_variants() {
610        use core::fmt::Write;
611        let variants: Vec<Value> = vec![
612            Value::Null,
613            Value::Bool(true),
614            Value::Float(1.0),
615            Value::Text("s".into()),
616            Value::Array(vec![]),
617            Value::Map(vec![]),
618            Value::U128(1),
619            Value::I128(1),
620            Value::U64(1),
621            Value::I64(1),
622            Value::U32(1),
623            Value::I32(1),
624            Value::U16(1),
625            Value::I16(1),
626            Value::U8(1),
627            Value::I8(1),
628            Value::Bytes(vec![]),
629            Value::Bytes20([0u8; 20]),
630            Value::Bytes32([0u8; 32]),
631            Value::Bytes36([0u8; 36]),
632            Value::Identifier([0u8; 32]),
633            Value::EnumU8(vec![]),
634            Value::EnumString(vec![]),
635        ];
636        for v in &variants {
637            let t = Type(v);
638            let mut buf = String::new();
639            write!(buf, "{}", t).unwrap();
640            assert!(!buf.is_empty());
641        }
642    }
643}