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)]
441mod test {
442 use serde_json::json;
443
444 use super::*;
445
446 #[test]
447 fn insert_with_parents() {
448 let mut document = json!({
449 "root" : {
450 "from" : {
451 "id": "123",
452 "message": "text_message",
453 },
454 }
455 });
456
457 document
458 .insert_with_path("root.to.new_field", json!("new_value"))
459 .expect("no errors");
460 document
461 .insert_with_path("root.array[0].new_field", json!("new_value"))
462 .expect("no errors");
463
464 assert_eq!(document["root"]["from"]["id"], json!("123"));
465 assert_eq!(document["root"]["from"]["message"], json!("text_message"));
466 assert_eq!(document["root"]["to"]["new_field"], json!("new_value"));
467 assert_eq!(
468 document["root"]["array"][0]["new_field"],
469 json!("new_value")
470 );
471 }
472
473 #[test]
478 fn push_adds_to_array() {
479 let mut arr = json!([1, 2, 3]);
480 arr.push(json!(4)).expect("should push");
481 assert_eq!(arr, json!([1, 2, 3, 4]));
482 }
483
484 #[test]
485 fn push_on_non_array_fails() {
486 let mut obj = json!({"a": 1});
487 assert!(obj.push(json!(1)).is_err());
488 }
489
490 #[test]
493 fn insert_adds_key_to_object() {
494 let mut obj = json!({"existing": 1});
495 JsonValueExt::insert(&mut obj, "new_key".to_string(), json!("new_value"))
496 .expect("should insert");
497 assert_eq!(obj["new_key"], json!("new_value"));
498 assert_eq!(obj["existing"], json!(1));
499 }
500
501 #[test]
502 fn insert_on_non_object_fails() {
503 let mut arr = json!([1, 2]);
504 assert!(JsonValueExt::insert(&mut arr, "key".to_string(), json!(1)).is_err());
505 }
506
507 #[test]
510 fn remove_existing_property() {
511 let mut obj = json!({"a": 1, "b": 2});
512 let removed = JsonValueExt::remove(&mut obj, "a").expect("should remove");
513 assert_eq!(removed, json!(1));
514 assert!(obj.get("a").is_none());
515 }
516
517 #[test]
518 fn remove_nonexistent_property_fails() {
519 let mut obj = json!({"a": 1});
520 assert!(JsonValueExt::remove(&mut obj, "missing").is_err());
521 }
522
523 #[test]
524 fn remove_on_non_object_fails() {
525 let mut val = json!("string");
526 assert!(JsonValueExt::remove(&mut val, "key").is_err());
527 }
528
529 #[test]
532 fn remove_into_deserializes_value() {
533 let mut obj = json!({"count": 42});
534 let count: u64 = obj
535 .remove_into("count")
536 .expect("should remove and deserialize");
537 assert_eq!(count, 42);
538 }
539
540 #[test]
541 fn remove_into_missing_property_fails() {
542 let mut obj = json!({"a": 1});
543 let result: Result<u64, _> = obj.remove_into("missing");
544 assert!(result.is_err());
545 }
546
547 #[test]
548 fn remove_into_on_non_object_fails() {
549 let mut val = json!(123);
550 let result: Result<u64, _> = val.remove_into("key");
551 assert!(result.is_err());
552 }
553
554 #[test]
557 fn get_string_returns_string_value() {
558 let obj = json!({"name": "Alice"});
559 let result = obj.get_string("name").expect("should get string");
560 assert_eq!(result, "Alice");
561 }
562
563 #[test]
564 fn get_string_missing_property_fails() {
565 let obj = json!({"a": 1});
566 assert!(obj.get_string("missing").is_err());
567 }
568
569 #[test]
570 fn get_string_non_string_value_fails() {
571 let obj = json!({"num": 42});
572 assert!(obj.get_string("num").is_err());
573 }
574
575 #[test]
578 fn get_u8_returns_u8_value() {
579 let obj = json!({"val": 200});
580 let result = obj.get_u8("val").expect("should get u8");
581 assert_eq!(result, 200);
582 }
583
584 #[test]
585 fn get_u8_too_large_fails() {
586 let obj = json!({"val": 300});
587 assert!(obj.get_u8("val").is_err());
588 }
589
590 #[test]
591 fn get_u8_missing_property_fails() {
592 let obj = json!({});
593 assert!(obj.get_u8("val").is_err());
594 }
595
596 #[test]
597 fn get_u8_non_number_fails() {
598 let obj = json!({"val": "text"});
599 assert!(obj.get_u8("val").is_err());
600 }
601
602 #[test]
605 fn get_u32_returns_u32_value() {
606 let obj = json!({"val": 100000});
607 let result = obj.get_u32("val").expect("should get u32");
608 assert_eq!(result, 100_000);
609 }
610
611 #[test]
612 fn get_u32_missing_property_fails() {
613 let obj = json!({});
614 assert!(obj.get_u32("val").is_err());
615 }
616
617 #[test]
618 fn get_u32_non_number_fails() {
619 let obj = json!({"val": true});
620 assert!(obj.get_u32("val").is_err());
621 }
622
623 #[test]
626 fn get_u64_returns_u64_value() {
627 let obj = json!({"val": 9999999999u64});
628 let result = obj.get_u64("val").expect("should get u64");
629 assert_eq!(result, 9_999_999_999);
630 }
631
632 #[test]
633 fn get_u64_missing_property_fails() {
634 let obj = json!({});
635 assert!(obj.get_u64("val").is_err());
636 }
637
638 #[test]
639 fn get_u64_non_number_fails() {
640 let obj = json!({"val": "text"});
641 assert!(obj.get_u64("val").is_err());
642 }
643
644 #[test]
647 fn get_i64_returns_i64_value() {
648 let obj = json!({"val": -42});
649 let result = obj.get_i64("val").expect("should get i64");
650 assert_eq!(result, -42);
651 }
652
653 #[test]
654 fn get_i64_positive_value() {
655 let obj = json!({"val": 100});
656 let result = obj.get_i64("val").expect("should get i64");
657 assert_eq!(result, 100);
658 }
659
660 #[test]
661 fn get_i64_missing_property_fails() {
662 let obj = json!({});
663 assert!(obj.get_i64("val").is_err());
664 }
665
666 #[test]
667 fn get_i64_non_number_fails() {
668 let obj = json!({"val": "text"});
669 assert!(obj.get_i64("val").is_err());
670 }
671
672 #[test]
675 fn get_f64_returns_f64_value() {
676 let obj = json!({"val": 3.14});
677 let result = obj.get_f64("val").expect("should get f64");
678 assert!((result - 3.14).abs() < f64::EPSILON);
679 }
680
681 #[test]
682 fn get_f64_integer_value() {
683 let obj = json!({"val": 42});
684 let result = obj.get_f64("val").expect("should get f64 from integer");
685 assert!((result - 42.0).abs() < f64::EPSILON);
686 }
687
688 #[test]
689 fn get_f64_missing_property_fails() {
690 let obj = json!({});
691 assert!(obj.get_f64("val").is_err());
692 }
693
694 #[test]
695 fn get_f64_non_number_fails() {
696 let obj = json!({"val": "text"});
697 assert!(obj.get_f64("val").is_err());
698 }
699
700 #[test]
703 fn get_bytes_from_array() {
704 let obj = json!({"data": [1, 2, 3, 4, 5]});
705 let result = obj.get_bytes("data").expect("should get bytes");
706 assert_eq!(result, vec![1, 2, 3, 4, 5]);
707 }
708
709 #[test]
710 fn get_bytes_missing_property_fails() {
711 let obj = json!({});
712 assert!(obj.get_bytes("data").is_err());
713 }
714
715 #[test]
718 fn get_bool_true() {
719 let obj = json!({"flag": true});
720 let result = obj.get_bool("flag").expect("should get bool");
721 assert!(result);
722 }
723
724 #[test]
725 fn get_bool_false() {
726 let obj = json!({"flag": false});
727 let result = obj.get_bool("flag").expect("should get bool");
728 assert!(!result);
729 }
730
731 #[test]
732 fn get_bool_missing_property_fails() {
733 let obj = json!({});
734 assert!(obj.get_bool("flag").is_err());
735 }
736
737 #[test]
738 fn get_bool_non_bool_fails() {
739 let obj = json!({"flag": 1});
740 assert!(obj.get_bool("flag").is_err());
741 }
742
743 #[test]
746 fn get_value_navigates_nested_path() {
747 let obj = json!({
748 "a": {
749 "b": {
750 "c": "deep_value"
751 }
752 }
753 });
754
755 let result = obj.get_value("a.b.c").expect("should find nested value");
756 assert_eq!(result, &json!("deep_value"));
757 }
758
759 #[test]
760 fn get_value_with_array_index() {
761 let obj = json!({
762 "items": [10, 20, 30]
763 });
764
765 let result = obj
766 .get_value("items[1]")
767 .expect("should find array element");
768 assert_eq!(result, &json!(20));
769 }
770
771 #[test]
772 fn get_value_missing_path_fails() {
773 let obj = json!({"a": 1});
774 assert!(obj.get_value("a.b.c").is_err());
775 }
776
777 #[test]
778 fn get_value_mut_modifies_nested_value() {
779 let mut obj = json!({
780 "a": {
781 "b": "old"
782 }
783 });
784
785 let val = obj.get_value_mut("a.b").expect("should find value");
786 *val = json!("new");
787
788 assert_eq!(obj["a"]["b"], json!("new"));
789 }
790
791 #[test]
794 fn get_value_by_path_with_steps() {
795 let obj = json!({
796 "root": {
797 "items": [1, 2, 3]
798 }
799 });
800
801 let path = vec![
802 JsonPathStep::Key("root".to_string()),
803 JsonPathStep::Key("items".to_string()),
804 JsonPathStep::Index(2),
805 ];
806
807 let result = obj.get_value_by_path(&path).expect("should find value");
808 assert_eq!(result, &json!(3));
809 }
810
811 #[test]
812 fn get_value_by_path_missing_fails() {
813 let obj = json!({"a": 1});
814 let path = vec![JsonPathStep::Key("nonexistent".to_string())];
815 assert!(obj.get_value_by_path(&path).is_err());
816 }
817
818 #[test]
819 fn get_value_by_path_mut_modifies_value() {
820 let mut obj = json!({
821 "list": ["a", "b", "c"]
822 });
823
824 let path = vec![
825 JsonPathStep::Key("list".to_string()),
826 JsonPathStep::Index(1),
827 ];
828
829 let val = obj.get_value_by_path_mut(&path).expect("should find value");
830 *val = json!("modified");
831
832 assert_eq!(obj["list"][1], json!("modified"));
833 }
834
835 #[test]
838 fn remove_u32_removes_and_returns_value() {
839 let mut obj = json!({"version": 42});
840 let result = obj.remove_u32("version").expect("should remove u32");
841 assert_eq!(result, 42);
842 assert!(obj.get("version").is_none());
843 }
844
845 #[test]
846 fn remove_u32_missing_property_fails() {
847 let mut obj = json!({"a": 1});
848 assert!(obj.remove_u32("missing").is_err());
849 }
850
851 #[test]
852 fn remove_u32_non_number_fails() {
853 let mut obj = json!({"val": "text"});
854 assert!(obj.remove_u32("val").is_err());
855 }
856
857 #[test]
858 fn remove_u32_on_non_object_fails() {
859 let mut val = json!([1, 2, 3]);
860 assert!(val.remove_u32("key").is_err());
861 }
862
863 #[test]
866 fn add_protocol_version_inserts_number() {
867 let mut obj = json!({});
868 obj.add_protocol_version("protocolVersion", 5)
869 .expect("should add protocol version");
870
871 assert_eq!(obj["protocolVersion"], json!(5));
872 }
873
874 #[test]
875 fn add_protocol_version_on_non_object_fails() {
876 let mut val = json!([1, 2]);
877 assert!(val.add_protocol_version("protocolVersion", 1).is_err());
878 }
879
880 #[test]
883 fn remove_value_at_path_into_removes_and_deserializes() {
884 let mut obj = json!({
885 "config": {
886 "timeout": 30
887 }
888 });
889
890 let timeout: u64 = obj
891 .remove_value_at_path_into("config.timeout")
892 .expect("should remove and deserialize");
893 assert_eq!(timeout, 30);
894 assert!(obj["config"].get("timeout").is_none());
895 }
896
897 #[test]
898 fn remove_value_at_path_into_missing_path_fails() {
899 let mut obj = json!({"a": 1});
900 let result: Result<u64, _> = obj.remove_value_at_path_into("a.b.c");
901 assert!(result.is_err());
902 }
903
904 #[test]
907 fn free_get_value_mut_works() {
908 let mut obj = json!({"x": {"y": 10}});
909 let val = get_value_mut("x.y", &mut obj).expect("should find value");
910 *val = json!(20);
911 assert_eq!(obj["x"]["y"], json!(20));
912 }
913
914 #[test]
915 fn get_value_from_json_path_navigates_keys_and_indices() {
916 let obj = json!({
917 "data": [
918 {"id": "first"},
919 {"id": "second"}
920 ]
921 });
922
923 let path = vec![
924 JsonPathStep::Key("data".to_string()),
925 JsonPathStep::Index(1),
926 JsonPathStep::Key("id".to_string()),
927 ];
928
929 let result = get_value_from_json_path(&path, &obj).expect("should find");
930 assert_eq!(result, &json!("second"));
931 }
932
933 #[test]
934 fn get_value_from_json_path_returns_none_for_missing() {
935 let obj = json!({"a": 1});
936 let path = vec![JsonPathStep::Key("nonexistent".to_string())];
937 assert!(get_value_from_json_path(&path, &obj).is_none());
938 }
939
940 #[test]
941 fn get_value_from_json_path_mut_modifies_value() {
942 let mut obj = json!({"items": [100, 200, 300]});
943
944 let path = vec![
945 JsonPathStep::Key("items".to_string()),
946 JsonPathStep::Index(0),
947 ];
948
949 let val = get_value_from_json_path_mut(&path, &mut obj).expect("should find");
950 *val = json!(999);
951 assert_eq!(obj["items"][0], json!(999));
952 }
953
954 #[test]
955 fn get_value_from_json_path_empty_path_returns_root() {
956 let obj = json!({"a": 1});
957 let path: Vec<JsonPathStep> = vec![];
958 let result = get_value_from_json_path(&path, &obj).expect("should return root");
959 assert_eq!(result, &json!({"a": 1}));
960 }
961
962 #[test]
965 fn insert_with_path_creates_deeply_nested_structure() {
966 let mut obj = json!({});
967 obj.insert_with_path("a.b.c", json!("deep"))
968 .expect("should insert");
969 assert_eq!(obj["a"]["b"]["c"], json!("deep"));
970 }
971
972 #[test]
973 fn insert_with_path_into_existing_structure() {
974 let mut obj = json!({"a": {"b": 1}});
975 obj.insert_with_path("a.c", json!(2))
976 .expect("should insert sibling");
977 assert_eq!(obj["a"]["c"], json!(2));
978 assert_eq!(obj["a"]["b"], json!(1));
979 }
980}