Skip to main content

dpp/util/json_value/
mod.rs

1use std::convert::TryInto;
2
3use anyhow::{anyhow, bail};
4
5use serde::de::DeserializeOwned;
6use serde_json::{Number, Value as JsonValue};
7
8use crate::{
9    errors::ProtocolError,
10    identifier::{self},
11};
12
13use super::json_path::{JsonPath, JsonPathLiteral, JsonPathStep};
14
15mod insert_with_path;
16use insert_with_path::*;
17
18mod remove_path;
19use remove_path::*;
20
21const PROPERTY_CONTENT_MEDIA_TYPE: &str = "contentMediaType";
22const PROPERTY_PROTOCOL_VERSION: &str = "protocolVersion";
23
24/// JsonValueExt contains a set of helper methods that simplify work with JsonValue
25pub trait JsonValueExt {
26    /// assumes the Json Value is a map and tries to remove the given property
27    fn remove(&mut self, property_name: &str) -> Result<JsonValue, anyhow::Error>;
28    /// assumes the Json Value is a map and tries to remove the given property and deserialize into the provided type
29    fn remove_into<K: DeserializeOwned>(&mut self, property_name: &str)
30        -> Result<K, anyhow::Error>;
31    /// assumes the Json Value is a map and tries to insert the given value under given property
32    fn insert(&mut self, property_name: String, value: JsonValue) -> Result<(), anyhow::Error>;
33    /// assumes the Json Value is an array and tries to add value to the array
34    fn push(&mut self, value: JsonValue) -> Result<(), anyhow::Error>;
35    fn get_string(&self, property_name: &str) -> Result<&str, anyhow::Error>;
36    fn get_i64(&self, property_name: &str) -> Result<i64, anyhow::Error>;
37    fn get_f64(&self, property_name: &str) -> Result<f64, anyhow::Error>;
38    fn get_u8(&self, property_name: &str) -> Result<u8, anyhow::Error>;
39    fn get_u32(&self, property_name: &str) -> Result<u32, anyhow::Error>;
40    fn get_u64(&self, property_name: &str) -> Result<u64, anyhow::Error>;
41    fn get_bytes(&self, property_name: &str) -> Result<Vec<u8>, anyhow::Error>;
42    /// returns the the mutable JsonValue from provided path. The path is dot-separated string. i.e `properties.id`
43    fn get_value_mut(&mut self, string_path: &str) -> Result<&mut JsonValue, anyhow::Error>;
44    /// returns the the JsonValue from provided path. The path is dot-separated string. i.e `properties[0].id`
45    fn get_value(&self, string_path: &str) -> Result<&JsonValue, anyhow::Error>;
46    /// return  the JsonValue from from provided path. The path is a slice of [`JsonPathStep`]
47    fn get_value_by_path(&self, path: &[JsonPathStep]) -> Result<&JsonValue, anyhow::Error>;
48    /// return  the mutable JsonValue from from provided path. The path is a slice of [`JsonPathStep`]
49    fn get_value_by_path_mut(
50        &mut self,
51        path: &[JsonPathStep],
52    ) -> Result<&mut JsonValue, anyhow::Error>;
53
54    /// assumes that the JsonValue is a Map and tries to remove the u32
55    fn remove_u32(&mut self, property_name: &str) -> Result<u32, anyhow::Error>;
56
57    fn add_protocol_version(
58        &mut self,
59        property_name: &str,
60        protocol_version: u32,
61    ) -> Result<(), ProtocolError>;
62
63    /// Insert value under the path. Path is dot-separated string. i.e `properties[0].id`. If parents don't
64    /// exists they will be created
65    fn insert_with_path(&mut self, path: &str, value: JsonValue) -> Result<(), anyhow::Error>;
66
67    /// Removes data from given path and tries deserialize it into provided type
68    fn remove_value_at_path_into<K: DeserializeOwned>(
69        &mut self,
70        property_name: &str,
71    ) -> Result<K, anyhow::Error>;
72    fn get_bool(&self, property_name: &str) -> Result<bool, anyhow::Error>;
73}
74
75impl JsonValueExt for JsonValue {
76    fn push(&mut self, value: JsonValue) -> Result<(), anyhow::Error> {
77        match self.as_array_mut() {
78            Some(map) => {
79                map.push(value);
80                Ok(())
81            }
82            None => bail!("data isn't an array: '{:?}'", self),
83        }
84    }
85
86    fn insert(&mut self, property_name: String, value: JsonValue) -> Result<(), anyhow::Error> {
87        match self.as_object_mut() {
88            Some(map) => {
89                map.insert(property_name, value);
90                Ok(())
91            }
92            None => bail!(
93                "getting property '{}' failed: the data isn't a map: '{:?}'",
94                self,
95                property_name
96            ),
97        }
98    }
99
100    fn remove_into<K: DeserializeOwned>(
101        &mut self,
102        property_name: &str,
103    ) -> Result<K, anyhow::Error> {
104        match self.as_object_mut() {
105            Some(map) => {
106                if let Some(data) = map.remove(property_name) {
107                    serde_json::from_value(data)
108                        .map_err(|err| anyhow!("unable convert data: {}`", err))
109                } else {
110                    bail!(
111                        "the property '{}' doesn't exist in {:?}",
112                        property_name,
113                        self
114                    )
115                }
116            }
117            None => bail!("the property '{}' isn't a map: '{:?}'", property_name, self),
118        }
119    }
120
121    fn remove(&mut self, property_name: &str) -> Result<JsonValue, anyhow::Error> {
122        match self.as_object_mut() {
123            Some(map) => map.remove(property_name).ok_or_else(|| {
124                anyhow!(
125                    "the property '{}' doesn't exists in '{:?}'",
126                    property_name,
127                    self
128                )
129            }),
130            None => bail!("the property '{}' isn't a map: '{:?}'", property_name, self),
131        }
132    }
133
134    fn get_string(&self, property_name: &str) -> Result<&str, anyhow::Error> {
135        let property_value = self.get(property_name).ok_or_else(|| {
136            anyhow!(
137                "the property '{}' doesn't exist in {:?}",
138                property_name,
139                self
140            )
141        })?;
142
143        if let JsonValue::String(s) = property_value {
144            return Ok(s);
145        }
146        bail!(
147            "getting property '{}' failed: {:?} isn't a String",
148            property_name,
149            property_value
150        );
151    }
152
153    fn get_u8(&self, property_name: &str) -> Result<u8, anyhow::Error> {
154        let property_value = self.get(property_name).ok_or_else(|| {
155            anyhow!(
156                "the property '{}' doesn't exist in '{:?}'",
157                property_name,
158                self
159            )
160        })?;
161
162        if let JsonValue::Number(s) = property_value {
163            return s
164                .as_u64()
165                .ok_or_else(|| anyhow!("unable convert {} to u64", s))?
166                .try_into()
167                .map_err(|e| anyhow!("unable convert {} to u8: {}", s, e));
168        }
169        bail!(
170            "getting property '{}' failed: {:?} isn't a number",
171            property_name,
172            property_value
173        );
174    }
175
176    fn get_u32(&self, property_name: &str) -> Result<u32, anyhow::Error> {
177        let property_value = self.get(property_name).ok_or_else(|| {
178            anyhow!(
179                "the property '{}' doesn't exist in '{:?}'",
180                property_name,
181                self
182            )
183        })?;
184
185        if let JsonValue::Number(s) = property_value {
186            return s
187                .as_u64()
188                .ok_or_else(|| anyhow!("unable convert {} to u64", s))?
189                .try_into()
190                .map_err(|e| anyhow!("unable convert {} to u32: {}", s, e));
191        }
192        bail!(
193            "getting property '{}' failed: {:?} isn't a number",
194            property_name,
195            property_value
196        );
197    }
198
199    fn get_u64(&self, property_name: &str) -> Result<u64, anyhow::Error> {
200        let property_value = self.get(property_name).ok_or_else(|| {
201            anyhow!(
202                "the property '{}' doesn't exist in '{:?}'",
203                property_name,
204                self
205            )
206        })?;
207
208        if let JsonValue::Number(s) = property_value {
209            return s
210                .as_u64()
211                .ok_or_else(|| anyhow!("unable convert {} to u64", s));
212        }
213        bail!(
214            "getting property '{}' failed: {:?} isn't a number",
215            property_name,
216            property_value
217        );
218    }
219
220    fn get_i64(&self, property_name: &str) -> Result<i64, anyhow::Error> {
221        let property_value = self.get(property_name).ok_or_else(|| {
222            anyhow!(
223                "the property '{}' doesn't exist in '{:?}'",
224                property_name,
225                self
226            )
227        })?;
228
229        if let JsonValue::Number(s) = property_value {
230            return s
231                .as_i64()
232                .ok_or_else(|| anyhow!("unable convert {} to i64", s));
233        }
234        bail!(
235            "getting property '{}' failed: {:?} isn't a number",
236            property_name,
237            property_value
238        );
239    }
240
241    fn get_f64(&self, property_name: &str) -> Result<f64, anyhow::Error> {
242        let property_value = self.get(property_name).ok_or_else(|| {
243            anyhow!(
244                "the property '{}' doesn't exist in '{:?}'",
245                property_name,
246                self
247            )
248        })?;
249
250        if let JsonValue::Number(s) = property_value {
251            return s
252                .as_f64()
253                .ok_or_else(|| anyhow!("unable convert {} to f64", s));
254        }
255        bail!(
256            "getting property '{}' failed: {:?} isn't a number",
257            property_name,
258            property_value
259        );
260    }
261
262    // TODO this method has an additional allocation which should be avoided
263    fn get_bytes(&self, property_name: &str) -> Result<Vec<u8>, anyhow::Error> {
264        let property_value = self.get(property_name).ok_or_else(|| {
265            anyhow!(
266                "the property '{}' doesn't exist in '{:?}'",
267                property_name,
268                self
269            )
270        })?;
271
272        serde_json::from_value(property_value.clone())
273            .map_err(|e| anyhow!("getting property '{}' failed: {}", property_name, e))
274    }
275
276    /// returns the value from the JsonValue based on the path: i.e "root.data[0].id"
277    fn get_value_mut(&mut self, string_path: &str) -> Result<&mut JsonValue, anyhow::Error> {
278        let path_literal: JsonPathLiteral = string_path.into();
279        let path: JsonPath = path_literal.try_into().unwrap();
280        get_value_from_json_path_mut(&path, self)
281            .ok_or_else(|| anyhow!("the property '{}' not found", string_path))
282    }
283
284    /// returns the value from the JsonValue based on the path: i.e "root.data[0].id"
285    fn get_value(&self, string_path: &str) -> Result<&JsonValue, anyhow::Error> {
286        let path_literal: JsonPathLiteral = string_path.into();
287        let path: JsonPath = path_literal.try_into().unwrap();
288        get_value_from_json_path(&path, self)
289            .ok_or_else(|| anyhow!("the property '{}' not found", string_path))
290    }
291
292    /// returns the value from the JsonValue based on the path: i.e "root.data[0].id"
293    fn get_value_by_path(&self, path: &[JsonPathStep]) -> Result<&JsonValue, anyhow::Error> {
294        get_value_from_json_path(path, self)
295            .ok_or_else(|| anyhow!("the property '{:?}' not found", path))
296    }
297
298    fn get_value_by_path_mut(
299        &mut self,
300        path: &[JsonPathStep],
301    ) -> Result<&mut JsonValue, anyhow::Error> {
302        get_value_from_json_path_mut(path, self)
303            .ok_or_else(|| anyhow!("the property '{:?}' not found", path))
304    }
305
306    fn add_protocol_version<'a>(
307        &mut self,
308        property_name: &str,
309        protocol_version: u32,
310    ) -> Result<(), ProtocolError> {
311        match self {
312            JsonValue::Object(ref mut m) => {
313                m.insert(
314                    String::from(property_name),
315                    JsonValue::Number(Number::from(protocol_version)),
316                );
317            }
318            _ => return Err(anyhow!("The '{:?}' isn't a map", self).into()),
319        }
320
321        Ok(())
322    }
323
324    fn remove_u32(&mut self, property_name: &str) -> Result<u32, anyhow::Error> {
325        match self {
326            JsonValue::Object(ref mut m) => match m.remove(property_name) {
327                Some(JsonValue::Number(number)) => Ok(number.as_u64().ok_or_else(|| {
328                    anyhow!("unable to convert '{}' into unsigned integer", number)
329                })? as u32),
330                _ => {
331                    bail!("Unable to find '{}' in '{}'", property_name, self)
332                }
333            },
334            _ => bail!("the Json Value isn't a map: {:?}", self),
335        }
336    }
337
338    /// Insert value under the path. Path is dot-separated string. i.e `properties[0].id`
339    fn insert_with_path(
340        &mut self,
341        string_path: &str,
342        value: JsonValue,
343    ) -> Result<(), anyhow::Error> {
344        let path_literal: JsonPathLiteral = string_path.into();
345        let path: JsonPath = path_literal.try_into().unwrap();
346        insert_with_path(self, &path, value)
347    }
348
349    /// Removes the value under given path and tries to deserialize it into provided type
350    fn remove_value_at_path_into<K: DeserializeOwned>(
351        &mut self,
352        path: &str,
353    ) -> Result<K, anyhow::Error> {
354        let path_literal: JsonPathLiteral = path.into();
355        let json_path: JsonPath = path_literal.try_into().unwrap();
356
357        let data = remove_path(&json_path, self)
358            .ok_or_else(|| anyhow!("the '{path}' doesn't exists in '{self:#?}'"))?;
359
360        serde_json::from_value(data).map_err(|err| anyhow!("unable convert data: {}`", err))
361    }
362
363    fn get_bool(&self, property_name: &str) -> Result<bool, anyhow::Error> {
364        let property_value = self.get(property_name).ok_or_else(|| {
365            anyhow!(
366                "the property '{}' doesn't exist in '{:?}'",
367                property_name,
368                self
369            )
370        })?;
371
372        if let JsonValue::Bool(s) = property_value {
373            return Ok(*s);
374        }
375        bail!(
376            "getting property '{}' failed: {:?} isn't a boolean",
377            property_name,
378            property_value
379        );
380    }
381}
382
383fn identifier_filter(value: &JsonValue) -> bool {
384    if let JsonValue::Object(object) = value {
385        if let Some(JsonValue::String(media_type)) = object.get(PROPERTY_CONTENT_MEDIA_TYPE) {
386            return media_type == identifier::MEDIA_TYPE;
387        }
388    }
389    false
390}
391
392/// returns the value from the JsonValue based on the path: i.e "root.data[0].id"
393pub fn get_value_mut<'a>(string_path: &str, value: &'a mut JsonValue) -> Option<&'a mut JsonValue> {
394    let path_literal: JsonPathLiteral = string_path.into();
395    let path: JsonPath = path_literal.try_into().unwrap();
396    get_value_from_json_path_mut(&path, value)
397}
398
399/// returns the value from the JsonValue based on the JsonPath
400pub fn get_value_from_json_path_mut<'a>(
401    path: &[JsonPathStep],
402    value: &'a mut JsonValue,
403) -> Option<&'a mut JsonValue> {
404    let mut last_ptr: &mut JsonValue = value;
405
406    for step in path {
407        match step {
408            JsonPathStep::Index(index) => {
409                last_ptr = last_ptr.get_mut(index)?;
410            }
411
412            JsonPathStep::Key(key) => {
413                last_ptr = last_ptr.get_mut(key)?;
414            }
415        }
416    }
417    Some(last_ptr)
418}
419
420/// returns the value from the JsonValue based on the JsonPath
421pub fn get_value_from_json_path<'a>(
422    path: &[JsonPathStep],
423    value: &'a JsonValue,
424) -> Option<&'a JsonValue> {
425    let mut last_ptr: &JsonValue = value;
426
427    for step in path {
428        match step {
429            JsonPathStep::Index(index) => {
430                last_ptr = last_ptr.get(index)?;
431            }
432            JsonPathStep::Key(key) => {
433                last_ptr = last_ptr.get(key)?;
434            }
435        }
436    }
437    Some(last_ptr)
438}
439
440#[cfg(test)]
441#[allow(clippy::approx_constant)]
442mod test {
443    use serde_json::json;
444
445    use super::*;
446
447    #[test]
448    fn insert_with_parents() {
449        let mut document = json!({
450            "root" :  {
451                "from" : {
452                    "id": "123",
453                    "message": "text_message",
454                },
455            }
456        });
457
458        document
459            .insert_with_path("root.to.new_field", json!("new_value"))
460            .expect("no errors");
461        document
462            .insert_with_path("root.array[0].new_field", json!("new_value"))
463            .expect("no errors");
464
465        assert_eq!(document["root"]["from"]["id"], json!("123"));
466        assert_eq!(document["root"]["from"]["message"], json!("text_message"));
467        assert_eq!(document["root"]["to"]["new_field"], json!("new_value"));
468        assert_eq!(
469            document["root"]["array"][0]["new_field"],
470            json!("new_value")
471        );
472    }
473
474    // ------- New coverage tests -------
475
476    // push
477
478    #[test]
479    fn push_adds_to_array() {
480        let mut arr = json!([1, 2, 3]);
481        arr.push(json!(4)).expect("should push");
482        assert_eq!(arr, json!([1, 2, 3, 4]));
483    }
484
485    #[test]
486    fn push_on_non_array_fails() {
487        let mut obj = json!({"a": 1});
488        assert!(obj.push(json!(1)).is_err());
489    }
490
491    // insert
492
493    #[test]
494    fn insert_adds_key_to_object() {
495        let mut obj = json!({"existing": 1});
496        JsonValueExt::insert(&mut obj, "new_key".to_string(), json!("new_value"))
497            .expect("should insert");
498        assert_eq!(obj["new_key"], json!("new_value"));
499        assert_eq!(obj["existing"], json!(1));
500    }
501
502    #[test]
503    fn insert_on_non_object_fails() {
504        let mut arr = json!([1, 2]);
505        assert!(JsonValueExt::insert(&mut arr, "key".to_string(), json!(1)).is_err());
506    }
507
508    // remove
509
510    #[test]
511    fn remove_existing_property() {
512        let mut obj = json!({"a": 1, "b": 2});
513        let removed = JsonValueExt::remove(&mut obj, "a").expect("should remove");
514        assert_eq!(removed, json!(1));
515        assert!(obj.get("a").is_none());
516    }
517
518    #[test]
519    fn remove_nonexistent_property_fails() {
520        let mut obj = json!({"a": 1});
521        assert!(JsonValueExt::remove(&mut obj, "missing").is_err());
522    }
523
524    #[test]
525    fn remove_on_non_object_fails() {
526        let mut val = json!("string");
527        assert!(JsonValueExt::remove(&mut val, "key").is_err());
528    }
529
530    // remove_into
531
532    #[test]
533    fn remove_into_deserializes_value() {
534        let mut obj = json!({"count": 42});
535        let count: u64 = obj
536            .remove_into("count")
537            .expect("should remove and deserialize");
538        assert_eq!(count, 42);
539    }
540
541    #[test]
542    fn remove_into_missing_property_fails() {
543        let mut obj = json!({"a": 1});
544        let result: Result<u64, _> = obj.remove_into("missing");
545        assert!(result.is_err());
546    }
547
548    #[test]
549    fn remove_into_on_non_object_fails() {
550        let mut val = json!(123);
551        let result: Result<u64, _> = val.remove_into("key");
552        assert!(result.is_err());
553    }
554
555    // get_string
556
557    #[test]
558    fn get_string_returns_string_value() {
559        let obj = json!({"name": "Alice"});
560        let result = obj.get_string("name").expect("should get string");
561        assert_eq!(result, "Alice");
562    }
563
564    #[test]
565    fn get_string_missing_property_fails() {
566        let obj = json!({"a": 1});
567        assert!(obj.get_string("missing").is_err());
568    }
569
570    #[test]
571    fn get_string_non_string_value_fails() {
572        let obj = json!({"num": 42});
573        assert!(obj.get_string("num").is_err());
574    }
575
576    // get_u8
577
578    #[test]
579    fn get_u8_returns_u8_value() {
580        let obj = json!({"val": 200});
581        let result = obj.get_u8("val").expect("should get u8");
582        assert_eq!(result, 200);
583    }
584
585    #[test]
586    fn get_u8_too_large_fails() {
587        let obj = json!({"val": 300});
588        assert!(obj.get_u8("val").is_err());
589    }
590
591    #[test]
592    fn get_u8_missing_property_fails() {
593        let obj = json!({});
594        assert!(obj.get_u8("val").is_err());
595    }
596
597    #[test]
598    fn get_u8_non_number_fails() {
599        let obj = json!({"val": "text"});
600        assert!(obj.get_u8("val").is_err());
601    }
602
603    // get_u32
604
605    #[test]
606    fn get_u32_returns_u32_value() {
607        let obj = json!({"val": 100000});
608        let result = obj.get_u32("val").expect("should get u32");
609        assert_eq!(result, 100_000);
610    }
611
612    #[test]
613    fn get_u32_missing_property_fails() {
614        let obj = json!({});
615        assert!(obj.get_u32("val").is_err());
616    }
617
618    #[test]
619    fn get_u32_non_number_fails() {
620        let obj = json!({"val": true});
621        assert!(obj.get_u32("val").is_err());
622    }
623
624    // get_u64
625
626    #[test]
627    fn get_u64_returns_u64_value() {
628        let obj = json!({"val": 9999999999u64});
629        let result = obj.get_u64("val").expect("should get u64");
630        assert_eq!(result, 9_999_999_999);
631    }
632
633    #[test]
634    fn get_u64_missing_property_fails() {
635        let obj = json!({});
636        assert!(obj.get_u64("val").is_err());
637    }
638
639    #[test]
640    fn get_u64_non_number_fails() {
641        let obj = json!({"val": "text"});
642        assert!(obj.get_u64("val").is_err());
643    }
644
645    // get_i64
646
647    #[test]
648    fn get_i64_returns_i64_value() {
649        let obj = json!({"val": -42});
650        let result = obj.get_i64("val").expect("should get i64");
651        assert_eq!(result, -42);
652    }
653
654    #[test]
655    fn get_i64_positive_value() {
656        let obj = json!({"val": 100});
657        let result = obj.get_i64("val").expect("should get i64");
658        assert_eq!(result, 100);
659    }
660
661    #[test]
662    fn get_i64_missing_property_fails() {
663        let obj = json!({});
664        assert!(obj.get_i64("val").is_err());
665    }
666
667    #[test]
668    fn get_i64_non_number_fails() {
669        let obj = json!({"val": "text"});
670        assert!(obj.get_i64("val").is_err());
671    }
672
673    // get_f64
674
675    #[test]
676    fn get_f64_returns_f64_value() {
677        let obj = json!({"val": 3.14});
678        let result = obj.get_f64("val").expect("should get f64");
679        assert!((result - 3.14).abs() < f64::EPSILON);
680    }
681
682    #[test]
683    fn get_f64_integer_value() {
684        let obj = json!({"val": 42});
685        let result = obj.get_f64("val").expect("should get f64 from integer");
686        assert!((result - 42.0).abs() < f64::EPSILON);
687    }
688
689    #[test]
690    fn get_f64_missing_property_fails() {
691        let obj = json!({});
692        assert!(obj.get_f64("val").is_err());
693    }
694
695    #[test]
696    fn get_f64_non_number_fails() {
697        let obj = json!({"val": "text"});
698        assert!(obj.get_f64("val").is_err());
699    }
700
701    // get_bytes
702
703    #[test]
704    fn get_bytes_from_array() {
705        let obj = json!({"data": [1, 2, 3, 4, 5]});
706        let result = obj.get_bytes("data").expect("should get bytes");
707        assert_eq!(result, vec![1, 2, 3, 4, 5]);
708    }
709
710    #[test]
711    fn get_bytes_missing_property_fails() {
712        let obj = json!({});
713        assert!(obj.get_bytes("data").is_err());
714    }
715
716    // get_bool
717
718    #[test]
719    fn get_bool_true() {
720        let obj = json!({"flag": true});
721        let result = obj.get_bool("flag").expect("should get bool");
722        assert!(result);
723    }
724
725    #[test]
726    fn get_bool_false() {
727        let obj = json!({"flag": false});
728        let result = obj.get_bool("flag").expect("should get bool");
729        assert!(!result);
730    }
731
732    #[test]
733    fn get_bool_missing_property_fails() {
734        let obj = json!({});
735        assert!(obj.get_bool("flag").is_err());
736    }
737
738    #[test]
739    fn get_bool_non_bool_fails() {
740        let obj = json!({"flag": 1});
741        assert!(obj.get_bool("flag").is_err());
742    }
743
744    // get_value and get_value_mut
745
746    #[test]
747    fn get_value_navigates_nested_path() {
748        let obj = json!({
749            "a": {
750                "b": {
751                    "c": "deep_value"
752                }
753            }
754        });
755
756        let result = obj.get_value("a.b.c").expect("should find nested value");
757        assert_eq!(result, &json!("deep_value"));
758    }
759
760    #[test]
761    fn get_value_with_array_index() {
762        let obj = json!({
763            "items": [10, 20, 30]
764        });
765
766        let result = obj
767            .get_value("items[1]")
768            .expect("should find array element");
769        assert_eq!(result, &json!(20));
770    }
771
772    #[test]
773    fn get_value_missing_path_fails() {
774        let obj = json!({"a": 1});
775        assert!(obj.get_value("a.b.c").is_err());
776    }
777
778    #[test]
779    fn get_value_mut_modifies_nested_value() {
780        let mut obj = json!({
781            "a": {
782                "b": "old"
783            }
784        });
785
786        let val = obj.get_value_mut("a.b").expect("should find value");
787        *val = json!("new");
788
789        assert_eq!(obj["a"]["b"], json!("new"));
790    }
791
792    // get_value_by_path and get_value_by_path_mut
793
794    #[test]
795    fn get_value_by_path_with_steps() {
796        let obj = json!({
797            "root": {
798                "items": [1, 2, 3]
799            }
800        });
801
802        let path = vec![
803            JsonPathStep::Key("root".to_string()),
804            JsonPathStep::Key("items".to_string()),
805            JsonPathStep::Index(2),
806        ];
807
808        let result = obj.get_value_by_path(&path).expect("should find value");
809        assert_eq!(result, &json!(3));
810    }
811
812    #[test]
813    fn get_value_by_path_missing_fails() {
814        let obj = json!({"a": 1});
815        let path = vec![JsonPathStep::Key("nonexistent".to_string())];
816        assert!(obj.get_value_by_path(&path).is_err());
817    }
818
819    #[test]
820    fn get_value_by_path_mut_modifies_value() {
821        let mut obj = json!({
822            "list": ["a", "b", "c"]
823        });
824
825        let path = vec![
826            JsonPathStep::Key("list".to_string()),
827            JsonPathStep::Index(1),
828        ];
829
830        let val = obj.get_value_by_path_mut(&path).expect("should find value");
831        *val = json!("modified");
832
833        assert_eq!(obj["list"][1], json!("modified"));
834    }
835
836    // remove_u32
837
838    #[test]
839    fn remove_u32_removes_and_returns_value() {
840        let mut obj = json!({"version": 42});
841        let result = obj.remove_u32("version").expect("should remove u32");
842        assert_eq!(result, 42);
843        assert!(obj.get("version").is_none());
844    }
845
846    #[test]
847    fn remove_u32_missing_property_fails() {
848        let mut obj = json!({"a": 1});
849        assert!(obj.remove_u32("missing").is_err());
850    }
851
852    #[test]
853    fn remove_u32_non_number_fails() {
854        let mut obj = json!({"val": "text"});
855        assert!(obj.remove_u32("val").is_err());
856    }
857
858    #[test]
859    fn remove_u32_on_non_object_fails() {
860        let mut val = json!([1, 2, 3]);
861        assert!(val.remove_u32("key").is_err());
862    }
863
864    // add_protocol_version
865
866    #[test]
867    fn add_protocol_version_inserts_number() {
868        let mut obj = json!({});
869        obj.add_protocol_version("protocolVersion", 5)
870            .expect("should add protocol version");
871
872        assert_eq!(obj["protocolVersion"], json!(5));
873    }
874
875    #[test]
876    fn add_protocol_version_on_non_object_fails() {
877        let mut val = json!([1, 2]);
878        assert!(val.add_protocol_version("protocolVersion", 1).is_err());
879    }
880
881    // remove_value_at_path_into
882
883    #[test]
884    fn remove_value_at_path_into_removes_and_deserializes() {
885        let mut obj = json!({
886            "config": {
887                "timeout": 30
888            }
889        });
890
891        let timeout: u64 = obj
892            .remove_value_at_path_into("config.timeout")
893            .expect("should remove and deserialize");
894        assert_eq!(timeout, 30);
895        assert!(obj["config"].get("timeout").is_none());
896    }
897
898    #[test]
899    fn remove_value_at_path_into_missing_path_fails() {
900        let mut obj = json!({"a": 1});
901        let result: Result<u64, _> = obj.remove_value_at_path_into("a.b.c");
902        assert!(result.is_err());
903    }
904
905    // Free functions: get_value_mut, get_value_from_json_path, get_value_from_json_path_mut
906
907    #[test]
908    fn free_get_value_mut_works() {
909        let mut obj = json!({"x": {"y": 10}});
910        let val = get_value_mut("x.y", &mut obj).expect("should find value");
911        *val = json!(20);
912        assert_eq!(obj["x"]["y"], json!(20));
913    }
914
915    #[test]
916    fn get_value_from_json_path_navigates_keys_and_indices() {
917        let obj = json!({
918            "data": [
919                {"id": "first"},
920                {"id": "second"}
921            ]
922        });
923
924        let path = vec![
925            JsonPathStep::Key("data".to_string()),
926            JsonPathStep::Index(1),
927            JsonPathStep::Key("id".to_string()),
928        ];
929
930        let result = get_value_from_json_path(&path, &obj).expect("should find");
931        assert_eq!(result, &json!("second"));
932    }
933
934    #[test]
935    fn get_value_from_json_path_returns_none_for_missing() {
936        let obj = json!({"a": 1});
937        let path = vec![JsonPathStep::Key("nonexistent".to_string())];
938        assert!(get_value_from_json_path(&path, &obj).is_none());
939    }
940
941    #[test]
942    fn get_value_from_json_path_mut_modifies_value() {
943        let mut obj = json!({"items": [100, 200, 300]});
944
945        let path = vec![
946            JsonPathStep::Key("items".to_string()),
947            JsonPathStep::Index(0),
948        ];
949
950        let val = get_value_from_json_path_mut(&path, &mut obj).expect("should find");
951        *val = json!(999);
952        assert_eq!(obj["items"][0], json!(999));
953    }
954
955    #[test]
956    fn get_value_from_json_path_empty_path_returns_root() {
957        let obj = json!({"a": 1});
958        let path: Vec<JsonPathStep> = vec![];
959        let result = get_value_from_json_path(&path, &obj).expect("should return root");
960        assert_eq!(result, &json!({"a": 1}));
961    }
962
963    // insert_with_path via trait
964
965    #[test]
966    fn insert_with_path_creates_deeply_nested_structure() {
967        let mut obj = json!({});
968        obj.insert_with_path("a.b.c", json!("deep"))
969            .expect("should insert");
970        assert_eq!(obj["a"]["b"]["c"], json!("deep"));
971    }
972
973    #[test]
974    fn insert_with_path_into_existing_structure() {
975        let mut obj = json!({"a": {"b": 1}});
976        obj.insert_with_path("a.c", json!(2))
977            .expect("should insert sibling");
978        assert_eq!(obj["a"]["c"], json!(2));
979        assert_eq!(obj["a"]["b"], json!(1));
980    }
981}