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
24pub trait JsonValueExt {
26 fn remove(&mut self, property_name: &str) -> Result<JsonValue, anyhow::Error>;
28 fn remove_into<K: DeserializeOwned>(&mut self, property_name: &str)
30 -> Result<K, anyhow::Error>;
31 fn insert(&mut self, property_name: String, value: JsonValue) -> Result<(), anyhow::Error>;
33 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 fn get_value_mut(&mut self, string_path: &str) -> Result<&mut JsonValue, anyhow::Error>;
44 fn get_value(&self, string_path: &str) -> Result<&JsonValue, anyhow::Error>;
46 fn get_value_by_path(&self, path: &[JsonPathStep]) -> Result<&JsonValue, anyhow::Error>;
48 fn get_value_by_path_mut(
50 &mut self,
51 path: &[JsonPathStep],
52 ) -> Result<&mut JsonValue, anyhow::Error>;
53
54 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 fn insert_with_path(&mut self, path: &str, value: JsonValue) -> Result<(), anyhow::Error>;
66
67 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 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 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 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 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 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 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
392pub 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
399pub 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
420pub 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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}