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}