platform_value/btreemap_extensions/
btreemap_path_extensions.rs

1#[cfg(feature = "json")]
2use serde_json::Value as JsonValue;
3use std::borrow::Borrow;
4use std::convert::TryFrom;
5use std::iter::FromIterator;
6
7use std::collections::BTreeMap;
8#[cfg(feature = "json")]
9use std::convert::TryInto;
10
11use crate::value_map::ValueMapHelper;
12use crate::{Error, Identifier, Value};
13
14pub trait BTreeValueMapPathHelper {
15    fn get_at_path(&self, path: &str) -> Result<&Value, Error>;
16    fn get_optional_at_path(&self, path: &str) -> Result<Option<&Value>, Error>;
17    fn get_optional_identifier_at_path(&self, path: &str) -> Result<Option<[u8; 32]>, Error>;
18    fn get_identifier_at_path(&self, path: &str) -> Result<[u8; 32], Error>;
19    fn get_optional_string_at_path(&self, path: &str) -> Result<Option<String>, Error>;
20    fn get_string_at_path(&self, path: &str) -> Result<String, Error>;
21    fn get_optional_str_at_path(&self, path: &str) -> Result<Option<&str>, Error>;
22    fn get_str_at_path(&self, path: &str) -> Result<&str, Error>;
23    fn get_optional_float_at_path(&self, path: &str) -> Result<Option<f64>, Error>;
24    fn get_float_at_path(&self, path: &str) -> Result<f64, Error>;
25    fn get_optional_integer_at_path<T>(&self, path: &str) -> Result<Option<T>, Error>
26    where
27        T: TryFrom<i128>
28            + TryFrom<u128>
29            + TryFrom<u64>
30            + TryFrom<i64>
31            + TryFrom<u32>
32            + TryFrom<i32>
33            + TryFrom<u16>
34            + TryFrom<i16>
35            + TryFrom<u8>
36            + TryFrom<i8>;
37    fn get_integer_at_path<T>(&self, path: &str) -> Result<T, Error>
38    where
39        T: TryFrom<i128>
40            + TryFrom<u128>
41            + TryFrom<u64>
42            + TryFrom<i64>
43            + TryFrom<u32>
44            + TryFrom<i32>
45            + TryFrom<u16>
46            + TryFrom<i16>
47            + TryFrom<u8>
48            + TryFrom<i8>;
49    fn get_optional_bool_at_path(&self, path: &str) -> Result<Option<bool>, Error>;
50    fn get_bool_at_path(&self, path: &str) -> Result<bool, Error>;
51    fn get_optional_inner_value_array_at_path<'a, I: FromIterator<&'a Value>>(
52        &'a self,
53        path: &str,
54    ) -> Result<Option<I>, Error>;
55    fn get_inner_value_array_at_path<'a, I: FromIterator<&'a Value>>(
56        &'a self,
57        path: &str,
58    ) -> Result<I, Error>;
59    fn get_optional_inner_string_array_at_path<I: FromIterator<String>>(
60        &self,
61        path: &str,
62    ) -> Result<Option<I>, Error>;
63    fn get_inner_string_array_at_path<I: FromIterator<String>>(
64        &self,
65        path: &str,
66    ) -> Result<I, Error>;
67    fn get_optional_inner_borrowed_map_at_path(
68        &self,
69        path: &str,
70    ) -> Result<Option<&Vec<(Value, Value)>>, Error>;
71    fn get_optional_inner_borrowed_str_value_map_at_path<'a, I: FromIterator<(String, &'a Value)>>(
72        &'a self,
73        path: &str,
74    ) -> Result<Option<I>, Error>;
75    fn get_inner_borrowed_str_value_map_at_path<'a, I: FromIterator<(String, &'a Value)>>(
76        &'a self,
77        path: &str,
78    ) -> Result<I, Error>;
79    #[cfg(feature = "json")]
80    fn get_optional_inner_str_json_value_map_at_path<I: FromIterator<(String, JsonValue)>>(
81        &self,
82        path: &str,
83    ) -> Result<Option<I>, Error>;
84    #[cfg(feature = "json")]
85    fn get_inner_str_json_value_map_at_path<I: FromIterator<(String, JsonValue)>>(
86        &self,
87        path: &str,
88    ) -> Result<I, Error>;
89    fn get_optional_hash256_bytes_at_path(&self, path: &str) -> Result<Option<[u8; 32]>, Error>;
90    fn get_hash256_bytes_at_path(&self, path: &str) -> Result<[u8; 32], Error>;
91    fn get_optional_identifier_bytes_at_path(&self, path: &str) -> Result<Option<Vec<u8>>, Error>;
92    fn get_identifier_bytes_at_path(&self, path: &str) -> Result<Vec<u8>, Error>;
93    fn remove_optional_string_at_path(&mut self, path: &str) -> Result<Option<String>, Error>;
94    fn remove_string_at_path(&mut self, path: &str) -> Result<String, Error>;
95    fn remove_optional_float_at_path(&mut self, path: &str) -> Result<Option<f64>, Error>;
96    fn remove_float_at_path(&mut self, path: &str) -> Result<f64, Error>;
97    fn remove_optional_integer_at_path<T>(&mut self, path: &str) -> Result<Option<T>, Error>
98    where
99        T: TryFrom<i128>
100            + TryFrom<u128>
101            + TryFrom<u64>
102            + TryFrom<i64>
103            + TryFrom<u32>
104            + TryFrom<i32>
105            + TryFrom<u16>
106            + TryFrom<i16>
107            + TryFrom<u8>
108            + TryFrom<i8>;
109    fn remove_integer_at_path<T>(&mut self, path: &str) -> Result<T, Error>
110    where
111        T: TryFrom<i128>
112            + TryFrom<u128>
113            + TryFrom<u64>
114            + TryFrom<i64>
115            + TryFrom<u32>
116            + TryFrom<i32>
117            + TryFrom<u16>
118            + TryFrom<i16>
119            + TryFrom<u8>
120            + TryFrom<i8>;
121    fn remove_optional_hash256_bytes_at_path(
122        &mut self,
123        path: &str,
124    ) -> Result<Option<[u8; 32]>, Error>;
125    fn remove_hash256_bytes_at_path(&mut self, path: &str) -> Result<[u8; 32], Error>;
126    fn remove_optional_identifier_at_path(
127        &mut self,
128        path: &str,
129    ) -> Result<Option<Identifier>, Error>;
130    fn remove_identifier_at_path(&mut self, path: &str) -> Result<Identifier, Error>;
131    fn get_optional_bytes_at_path(&self, path: &str) -> Result<Option<Vec<u8>>, Error>;
132    fn get_bytes_at_path(&self, path: &str) -> Result<Vec<u8>, Error>;
133    fn get_optional_binary_bytes_at_path(&self, path: &str) -> Result<Option<Vec<u8>>, Error>;
134    fn get_binary_bytes_at_path(&self, path: &str) -> Result<Vec<u8>, Error>;
135}
136
137impl<V> BTreeValueMapPathHelper for BTreeMap<String, V>
138where
139    V: Borrow<Value>,
140{
141    fn get_at_path(&self, path: &str) -> Result<&Value, Error> {
142        let mut split = path.split('.');
143        let first = split.next();
144        let Some(first_path_component) = first else {
145            return Err(Error::PathError("path was empty".to_string()));
146        };
147        let mut current_value = self
148            .get(first_path_component)
149            .ok_or_else(|| {
150                Error::StructureError(format!(
151                    "unable to get property {first_path_component} in {path}"
152                ))
153            })?
154            .borrow();
155        for path_component in split {
156            let map = current_value.to_map_ref()?;
157            current_value = map.get_optional_key(path_component).ok_or_else(|| {
158                Error::StructureError(format!(
159                    "unable to get property at path {path_component} in {path}"
160                ))
161            })?;
162        }
163        Ok(current_value)
164    }
165
166    fn get_optional_at_path(&self, path: &str) -> Result<Option<&Value>, Error> {
167        let mut split = path.split('.');
168        let first = split.next();
169        let Some(first_path_component) = first else {
170            return Err(Error::PathError("path was empty".to_string()));
171        };
172        let Some(mut current_value) = self.get(first_path_component).map(|v| v.borrow()) else {
173            return Ok(None);
174        };
175        for path_component in split {
176            let map = current_value.to_map_ref()?;
177            let Some(new_value) = map.get_optional_key(path_component) else {
178                return Ok(None);
179            };
180            current_value = new_value;
181        }
182        Ok(Some(current_value))
183    }
184
185    fn get_optional_identifier_at_path(&self, path: &str) -> Result<Option<[u8; 32]>, Error> {
186        self.get_optional_at_path(path)?
187            .map(|v| v.to_hash256())
188            .transpose()
189    }
190
191    fn get_identifier_at_path(&self, path: &str) -> Result<[u8; 32], Error> {
192        self.get_optional_identifier_at_path(path)?.ok_or_else(|| {
193            Error::StructureError(format!("unable to get identifier property {path}"))
194        })
195    }
196
197    fn get_optional_string_at_path(&self, path: &str) -> Result<Option<String>, Error> {
198        self.get_optional_at_path(path)?
199            .map(|v| {
200                v.as_text()
201                    .map(|str| str.to_string())
202                    .ok_or_else(|| Error::StructureError(format!("{path} must be a string")))
203            })
204            .transpose()
205    }
206
207    fn get_string_at_path(&self, path: &str) -> Result<String, Error> {
208        self.get_optional_string_at_path(path)?
209            .ok_or_else(|| Error::StructureError(format!("unable to get string property {path}")))
210    }
211
212    fn get_optional_str_at_path(&self, path: &str) -> Result<Option<&str>, Error> {
213        self.get_optional_at_path(path)?
214            .map(|v| {
215                v.as_text()
216                    .ok_or_else(|| Error::StructureError(format!("{path} must be a string")))
217            })
218            .transpose()
219    }
220
221    fn get_str_at_path(&self, path: &str) -> Result<&str, Error> {
222        self.get_optional_str_at_path(path)?
223            .ok_or_else(|| Error::StructureError(format!("unable to get str property {path}")))
224    }
225
226    fn get_optional_integer_at_path<T>(&self, path: &str) -> Result<Option<T>, Error>
227    where
228        T: TryFrom<i128>
229            + TryFrom<u128>
230            + TryFrom<u64>
231            + TryFrom<i64>
232            + TryFrom<u32>
233            + TryFrom<i32>
234            + TryFrom<u16>
235            + TryFrom<i16>
236            + TryFrom<u8>
237            + TryFrom<i8>,
238    {
239        self.get_optional_at_path(path)?
240            .and_then(|v| {
241                if v.is_null() {
242                    None
243                } else {
244                    Some(v.to_integer())
245                }
246            })
247            .transpose()
248    }
249
250    fn get_integer_at_path<T>(&self, path: &str) -> Result<T, Error>
251    where
252        T: TryFrom<i128>
253            + TryFrom<u128>
254            + TryFrom<u64>
255            + TryFrom<i64>
256            + TryFrom<u32>
257            + TryFrom<i32>
258            + TryFrom<u16>
259            + TryFrom<i16>
260            + TryFrom<u8>
261            + TryFrom<i8>,
262    {
263        self.get_optional_integer_at_path(path)?
264            .ok_or_else(|| Error::StructureError(format!("unable to get integer property {path}")))
265    }
266
267    fn remove_optional_integer_at_path<T>(&mut self, path: &str) -> Result<Option<T>, Error>
268    where
269        T: TryFrom<i128>
270            + TryFrom<u128>
271            + TryFrom<u64>
272            + TryFrom<i64>
273            + TryFrom<u32>
274            + TryFrom<i32>
275            + TryFrom<u16>
276            + TryFrom<i16>
277            + TryFrom<u8>
278            + TryFrom<i8>,
279    {
280        self.remove(path)
281            .and_then(|v| {
282                let borrowed = v.borrow();
283                if borrowed.is_null() {
284                    None
285                } else {
286                    Some(v.borrow().to_integer())
287                }
288            })
289            .transpose()
290    }
291
292    fn remove_integer_at_path<T>(&mut self, path: &str) -> Result<T, Error>
293    where
294        T: TryFrom<i128>
295            + TryFrom<u128>
296            + TryFrom<u64>
297            + TryFrom<i64>
298            + TryFrom<u32>
299            + TryFrom<i32>
300            + TryFrom<u16>
301            + TryFrom<i16>
302            + TryFrom<u8>
303            + TryFrom<i8>,
304    {
305        self.remove_optional_integer_at_path(path)?.ok_or_else(|| {
306            Error::StructureError(format!("unable to remove integer property {path}"))
307        })
308    }
309
310    fn get_optional_bool_at_path(&self, path: &str) -> Result<Option<bool>, Error> {
311        self.get_optional_at_path(path)?
312            .and_then(|v| if v.is_null() { None } else { Some(v.to_bool()) })
313            .transpose()
314    }
315
316    fn get_bool_at_path(&self, path: &str) -> Result<bool, Error> {
317        self.get_optional_bool_at_path(path)?
318            .ok_or_else(|| Error::StructureError(format!("unable to get bool property {path}")))
319    }
320
321    fn get_optional_inner_value_array_at_path<'a, I: FromIterator<&'a Value>>(
322        &'a self,
323        path: &str,
324    ) -> Result<Option<I>, Error> {
325        self.get_optional_at_path(path)?
326            .map(|v| {
327                v.as_array()
328                    .map(|vec| vec.iter().collect())
329                    .ok_or_else(|| Error::StructureError(format!("{path} must be a bool")))
330            })
331            .transpose()
332    }
333
334    fn get_inner_value_array_at_path<'a, I: FromIterator<&'a Value>>(
335        &'a self,
336        path: &str,
337    ) -> Result<I, Error> {
338        self.get_optional_inner_value_array_at_path(path)?
339            .ok_or_else(|| {
340                Error::StructureError(format!("unable to get inner value array property {path}"))
341            })
342    }
343
344    fn get_optional_inner_string_array_at_path<I: FromIterator<String>>(
345        &self,
346        path: &str,
347    ) -> Result<Option<I>, Error> {
348        self.get_optional_at_path(path)?
349            .map(|v| {
350                v.as_array()
351                    .map(|inner| {
352                        inner
353                            .iter()
354                            .map(|v| {
355                                let Some(str) = v.as_text() else {
356                                    return Err(Error::StructureError(format!(
357                                        "{path} must be an string"
358                                    )));
359                                };
360                                Ok(str.to_string())
361                            })
362                            .collect::<Result<I, Error>>()
363                    })
364                    .transpose()?
365                    .ok_or_else(|| Error::StructureError(format!("{path} must be a bool")))
366            })
367            .transpose()
368    }
369
370    fn get_inner_string_array_at_path<I: FromIterator<String>>(
371        &self,
372        path: &str,
373    ) -> Result<I, Error> {
374        self.get_optional_inner_string_array_at_path(path)?
375            .ok_or_else(|| {
376                Error::StructureError(format!("unable to get inner string property {path}"))
377            })
378    }
379
380    fn get_optional_inner_borrowed_map_at_path(
381        &self,
382        path: &str,
383    ) -> Result<Option<&Vec<(Value, Value)>>, Error> {
384        self.get_optional_at_path(path)?
385            .map(|v| {
386                v.as_map()
387                    .ok_or_else(|| Error::StructureError(format!("{path} must be a map")))
388            })
389            .transpose()
390    }
391
392    fn get_optional_inner_borrowed_str_value_map_at_path<
393        'a,
394        I: FromIterator<(String, &'a Value)>,
395    >(
396        &'a self,
397        path: &str,
398    ) -> Result<Option<I>, Error> {
399        self.get_optional_at_path(path)?
400            .map(|v| {
401                v.as_map()
402                    .map(|inner| {
403                        inner
404                            .iter()
405                            .map(|(k, v)| Ok((k.to_text()?, v)))
406                            .collect::<Result<I, Error>>()
407                    })
408                    .transpose()?
409                    .ok_or_else(|| Error::StructureError(format!("{path} must be a bool")))
410            })
411            .transpose()
412    }
413
414    fn get_inner_borrowed_str_value_map_at_path<'a, I: FromIterator<(String, &'a Value)>>(
415        &'a self,
416        path: &str,
417    ) -> Result<I, Error> {
418        self.get_optional_inner_borrowed_str_value_map_at_path(path)?
419            .ok_or_else(|| {
420                Error::StructureError(format!(
421                    "unable to get borrowed str value map property {path}"
422                ))
423            })
424    }
425
426    #[cfg(feature = "json")]
427    fn get_optional_inner_str_json_value_map_at_path<I: FromIterator<(String, JsonValue)>>(
428        &self,
429        path: &str,
430    ) -> Result<Option<I>, Error> {
431        self.get_optional_at_path(path)?
432            .map(|v| {
433                v.as_map()
434                    .map(|inner| {
435                        inner
436                            .iter()
437                            .map(|(k, v)| Ok((k.to_text()?, v.clone().try_into()?)))
438                            .collect::<Result<I, Error>>()
439                    })
440                    .transpose()?
441                    .ok_or_else(|| Error::StructureError(format!("{path} must be a bool")))
442            })
443            .transpose()
444    }
445
446    #[cfg(feature = "json")]
447    fn get_inner_str_json_value_map_at_path<I: FromIterator<(String, JsonValue)>>(
448        &self,
449        path: &str,
450    ) -> Result<I, Error> {
451        self.get_optional_inner_str_json_value_map_at_path(path)?
452            .ok_or_else(|| {
453                Error::StructureError(format!(
454                    "unable to get borrowed str json value map property {path}"
455                ))
456            })
457    }
458
459    fn get_optional_hash256_bytes_at_path(&self, path: &str) -> Result<Option<[u8; 32]>, Error> {
460        self.get_optional_at_path(path)?
461            .map(|v| v.to_hash256())
462            .transpose()
463    }
464
465    fn get_hash256_bytes_at_path(&self, path: &str) -> Result<[u8; 32], Error> {
466        self.get_optional_hash256_bytes_at_path(path)?
467            .ok_or_else(|| Error::StructureError(format!("unable to get hash256 property {path}")))
468    }
469
470    fn get_optional_bytes_at_path(&self, path: &str) -> Result<Option<Vec<u8>>, Error> {
471        self.get_optional_at_path(path)?
472            .map(|v| v.to_bytes())
473            .transpose()
474    }
475
476    fn get_bytes_at_path(&self, path: &str) -> Result<Vec<u8>, Error> {
477        self.get_optional_bytes_at_path(path)?.ok_or_else(|| {
478            Error::StructureError(format!("unable to get system bytes property {path}"))
479        })
480    }
481
482    fn get_optional_identifier_bytes_at_path(&self, path: &str) -> Result<Option<Vec<u8>>, Error> {
483        self.get_optional_at_path(path)?
484            .map(|v| v.to_identifier_bytes())
485            .transpose()
486    }
487
488    fn get_identifier_bytes_at_path(&self, path: &str) -> Result<Vec<u8>, Error> {
489        self.get_optional_identifier_bytes_at_path(path)?
490            .ok_or_else(|| {
491                Error::StructureError(format!("unable to get system bytes property {path}"))
492            })
493    }
494
495    fn get_optional_binary_bytes_at_path(&self, path: &str) -> Result<Option<Vec<u8>>, Error> {
496        self.get_optional_at_path(path)?
497            .map(|v| v.to_binary_bytes())
498            .transpose()
499    }
500
501    fn get_binary_bytes_at_path(&self, path: &str) -> Result<Vec<u8>, Error> {
502        self.get_optional_binary_bytes_at_path(path)?
503            .ok_or_else(|| {
504                Error::StructureError(format!("unable to get system bytes property {path}"))
505            })
506    }
507
508    fn remove_optional_hash256_bytes_at_path(
509        &mut self,
510        path: &str,
511    ) -> Result<Option<[u8; 32]>, Error> {
512        self.remove(path)
513            .map(|v| v.borrow().to_hash256())
514            .transpose()
515    }
516
517    fn remove_hash256_bytes_at_path(&mut self, path: &str) -> Result<[u8; 32], Error> {
518        self.remove_optional_hash256_bytes_at_path(path)?
519            .ok_or_else(|| {
520                Error::StructureError(format!("unable to remove hash256 property {path}"))
521            })
522    }
523
524    fn remove_optional_identifier_at_path(
525        &mut self,
526        path: &str,
527    ) -> Result<Option<Identifier>, Error> {
528        self.remove(path)
529            .map(|v| v.borrow().to_identifier())
530            .transpose()
531    }
532
533    fn remove_identifier_at_path(&mut self, path: &str) -> Result<Identifier, Error> {
534        self.remove_optional_identifier_at_path(path)?
535            .ok_or_else(|| {
536                Error::StructureError(format!("unable to remove system bytes property {path}"))
537            })
538    }
539
540    fn remove_optional_string_at_path(&mut self, path: &str) -> Result<Option<String>, Error> {
541        self.remove(path).map(|v| v.borrow().to_text()).transpose()
542    }
543
544    fn remove_string_at_path(&mut self, path: &str) -> Result<String, Error> {
545        self.remove_optional_string_at_path(path)?.ok_or_else(|| {
546            Error::StructureError(format!("unable to remove string property {path}"))
547        })
548    }
549
550    fn remove_optional_float_at_path(&mut self, path: &str) -> Result<Option<f64>, Error> {
551        self.remove(path)
552            .and_then(|v| {
553                let borrowed = v.borrow();
554                if borrowed.is_null() {
555                    None
556                } else {
557                    Some(v.borrow().to_float())
558                }
559            })
560            .transpose()
561    }
562
563    fn remove_float_at_path(&mut self, path: &str) -> Result<f64, Error> {
564        self.remove_optional_float_at_path(path)?
565            .ok_or_else(|| Error::StructureError(format!("unable to remove float property {path}")))
566    }
567
568    fn get_optional_float_at_path(&self, path: &str) -> Result<Option<f64>, Error> {
569        self.get_optional_at_path(path)?
570            .and_then(|v| {
571                if v.is_null() {
572                    None
573                } else {
574                    Some(v.to_float())
575                }
576            })
577            .transpose()
578    }
579
580    fn get_float_at_path(&self, path: &str) -> Result<f64, Error> {
581        self.get_optional_float_at_path(path)?
582            .ok_or_else(|| Error::StructureError(format!("unable to get float property {path}")))
583    }
584}
585
586#[cfg(test)]
587mod tests {
588    use super::*;
589    use crate::{Identifier, Value};
590    use std::collections::BTreeMap;
591
592    // -----------------------------------------------------------------------
593    // Helper: build a BTreeMap<String, Value> from tuples
594    // -----------------------------------------------------------------------
595
596    fn map_with(entries: Vec<(&str, Value)>) -> BTreeMap<String, Value> {
597        entries
598            .into_iter()
599            .map(|(k, v)| (k.to_string(), v))
600            .collect()
601    }
602
603    /// Build a nested Value::Map from a BTreeMap-like structure
604    fn nested_value_map(entries: Vec<(&str, Value)>) -> Value {
605        Value::Map(
606            entries
607                .into_iter()
608                .map(|(k, v)| (Value::Text(k.to_string()), v))
609                .collect(),
610        )
611    }
612
613    // -----------------------------------------------------------------------
614    // get_at_path / get_optional_at_path
615    // -----------------------------------------------------------------------
616
617    #[test]
618    fn get_at_path_single_level() {
619        let map = map_with(vec![("name", Value::Text("alice".to_string()))]);
620        let result = map.get_at_path("name").unwrap();
621        assert_eq!(result, &Value::Text("alice".to_string()));
622    }
623
624    #[test]
625    fn get_at_path_nested() {
626        let inner = nested_value_map(vec![("city", Value::Text("NYC".to_string()))]);
627        let map = map_with(vec![("address", inner)]);
628        let result = map.get_at_path("address.city").unwrap();
629        assert_eq!(result, &Value::Text("NYC".to_string()));
630    }
631
632    #[test]
633    fn get_at_path_deeply_nested() {
634        let deep = nested_value_map(vec![("z", Value::U64(42))]);
635        let mid = nested_value_map(vec![("y", deep)]);
636        let map = map_with(vec![("x", mid)]);
637        let result = map.get_at_path("x.y.z").unwrap();
638        assert_eq!(result, &Value::U64(42));
639    }
640
641    #[test]
642    fn get_at_path_missing_top_level_errors() {
643        let map: BTreeMap<String, Value> = BTreeMap::new();
644        assert!(map.get_at_path("missing").is_err());
645    }
646
647    #[test]
648    fn get_at_path_missing_nested_key_errors() {
649        let inner = nested_value_map(vec![("a", Value::U64(1))]);
650        let map = map_with(vec![("top", inner)]);
651        assert!(map.get_at_path("top.nonexistent").is_err());
652    }
653
654    #[test]
655    fn get_at_path_empty_path_errors() {
656        let map = map_with(vec![("a", Value::U64(1))]);
657        // Empty string splits into one element "", which won't match anything
658        // The actual implementation: split("").next() returns Some("")
659        // but get("") returns None => StructureError
660        assert!(map.get_at_path("").is_err());
661    }
662
663    #[test]
664    fn get_optional_at_path_returns_some() {
665        let map = map_with(vec![("key", Value::Bool(true))]);
666        let result = map.get_optional_at_path("key").unwrap();
667        assert_eq!(result, Some(&Value::Bool(true)));
668    }
669
670    #[test]
671    fn get_optional_at_path_returns_none_for_missing_top_level() {
672        let map: BTreeMap<String, Value> = BTreeMap::new();
673        let result = map.get_optional_at_path("missing").unwrap();
674        assert_eq!(result, None);
675    }
676
677    #[test]
678    fn get_optional_at_path_returns_none_for_missing_nested() {
679        let inner = nested_value_map(vec![("a", Value::U64(1))]);
680        let map = map_with(vec![("top", inner)]);
681        let result = map.get_optional_at_path("top.missing").unwrap();
682        assert_eq!(result, None);
683    }
684
685    #[test]
686    fn get_optional_at_path_nested_returns_some() {
687        let inner = nested_value_map(vec![("val", Value::Float(9.9))]);
688        let map = map_with(vec![("nested", inner)]);
689        let result = map.get_optional_at_path("nested.val").unwrap();
690        assert_eq!(result, Some(&Value::Float(9.9)));
691    }
692
693    // -----------------------------------------------------------------------
694    // get_identifier_at_path / get_optional_identifier_at_path
695    // -----------------------------------------------------------------------
696
697    #[test]
698    fn get_identifier_at_path_success() {
699        let id = [1u8; 32];
700        let map = map_with(vec![("id", Value::Bytes32(id))]);
701        let result = map.get_identifier_at_path("id").unwrap();
702        assert_eq!(result, id);
703    }
704
705    #[test]
706    fn get_identifier_at_path_missing_errors() {
707        let map: BTreeMap<String, Value> = BTreeMap::new();
708        assert!(map.get_identifier_at_path("id").is_err());
709    }
710
711    #[test]
712    fn get_optional_identifier_at_path_returns_some() {
713        let id = [2u8; 32];
714        let map = map_with(vec![("id", Value::Bytes32(id))]);
715        let result = map.get_optional_identifier_at_path("id").unwrap();
716        assert_eq!(result, Some(id));
717    }
718
719    #[test]
720    fn get_optional_identifier_at_path_returns_none_for_missing() {
721        let map: BTreeMap<String, Value> = BTreeMap::new();
722        let result = map.get_optional_identifier_at_path("id").unwrap();
723        assert_eq!(result, None);
724    }
725
726    // -----------------------------------------------------------------------
727    // get_string_at_path / get_optional_string_at_path
728    // -----------------------------------------------------------------------
729
730    #[test]
731    fn get_string_at_path_success() {
732        let map = map_with(vec![("s", Value::Text("hello".to_string()))]);
733        let result = map.get_string_at_path("s").unwrap();
734        assert_eq!(result, "hello");
735    }
736
737    #[test]
738    fn get_string_at_path_missing_errors() {
739        let map: BTreeMap<String, Value> = BTreeMap::new();
740        assert!(map.get_string_at_path("s").is_err());
741    }
742
743    #[test]
744    fn get_optional_string_at_path_returns_some() {
745        let map = map_with(vec![("s", Value::Text("world".to_string()))]);
746        let result = map.get_optional_string_at_path("s").unwrap();
747        assert_eq!(result, Some("world".to_string()));
748    }
749
750    #[test]
751    fn get_optional_string_at_path_returns_none() {
752        let map: BTreeMap<String, Value> = BTreeMap::new();
753        let result = map.get_optional_string_at_path("s").unwrap();
754        assert_eq!(result, None);
755    }
756
757    #[test]
758    fn get_optional_string_at_path_wrong_type_errors() {
759        let map = map_with(vec![("s", Value::U64(42))]);
760        assert!(map.get_optional_string_at_path("s").is_err());
761    }
762
763    // -----------------------------------------------------------------------
764    // get_str_at_path / get_optional_str_at_path
765    // -----------------------------------------------------------------------
766
767    #[test]
768    fn get_str_at_path_success() {
769        let map = map_with(vec![("s", Value::Text("borrow_me".to_string()))]);
770        let result = map.get_str_at_path("s").unwrap();
771        assert_eq!(result, "borrow_me");
772    }
773
774    #[test]
775    fn get_str_at_path_missing_errors() {
776        let map: BTreeMap<String, Value> = BTreeMap::new();
777        assert!(map.get_str_at_path("s").is_err());
778    }
779
780    #[test]
781    fn get_optional_str_at_path_returns_some() {
782        let map = map_with(vec![("s", Value::Text("ref".to_string()))]);
783        let result = map.get_optional_str_at_path("s").unwrap();
784        assert_eq!(result, Some("ref"));
785    }
786
787    #[test]
788    fn get_optional_str_at_path_returns_none() {
789        let map: BTreeMap<String, Value> = BTreeMap::new();
790        let result = map.get_optional_str_at_path("s").unwrap();
791        assert_eq!(result, None);
792    }
793
794    // -----------------------------------------------------------------------
795    // get_integer_at_path / get_optional_integer_at_path
796    // -----------------------------------------------------------------------
797
798    #[test]
799    fn get_integer_at_path_success() {
800        let map = map_with(vec![("num", Value::U64(100))]);
801        let result: u64 = map.get_integer_at_path("num").unwrap();
802        assert_eq!(result, 100);
803    }
804
805    #[test]
806    fn get_integer_at_path_nested() {
807        let inner = nested_value_map(vec![("val", Value::I32(-5))]);
808        let map = map_with(vec![("data", inner)]);
809        let result: i32 = map.get_integer_at_path("data.val").unwrap();
810        assert_eq!(result, -5);
811    }
812
813    #[test]
814    fn get_integer_at_path_missing_errors() {
815        let map: BTreeMap<String, Value> = BTreeMap::new();
816        let result: Result<u64, Error> = map.get_integer_at_path("num");
817        assert!(result.is_err());
818    }
819
820    #[test]
821    fn get_optional_integer_at_path_returns_some() {
822        let map = map_with(vec![("n", Value::U32(7))]);
823        let result: Option<u32> = map.get_optional_integer_at_path("n").unwrap();
824        assert_eq!(result, Some(7));
825    }
826
827    #[test]
828    fn get_optional_integer_at_path_returns_none_for_missing() {
829        let map: BTreeMap<String, Value> = BTreeMap::new();
830        let result: Option<u32> = map.get_optional_integer_at_path("n").unwrap();
831        assert_eq!(result, None);
832    }
833
834    #[test]
835    fn get_optional_integer_at_path_returns_none_for_null() {
836        let map = map_with(vec![("n", Value::Null)]);
837        let result: Option<u32> = map.get_optional_integer_at_path("n").unwrap();
838        assert_eq!(result, None);
839    }
840
841    // -----------------------------------------------------------------------
842    // get_float_at_path / get_optional_float_at_path
843    // -----------------------------------------------------------------------
844
845    #[test]
846    fn get_float_at_path_success() {
847        let map = map_with(vec![("f", Value::Float(1.23))]);
848        let result = map.get_float_at_path("f").unwrap();
849        assert!((result - 1.23).abs() < f64::EPSILON);
850    }
851
852    #[test]
853    fn get_float_at_path_missing_errors() {
854        let map: BTreeMap<String, Value> = BTreeMap::new();
855        assert!(map.get_float_at_path("f").is_err());
856    }
857
858    #[test]
859    fn get_optional_float_at_path_returns_some() {
860        let map = map_with(vec![("f", Value::Float(2.72))]);
861        let result = map.get_optional_float_at_path("f").unwrap();
862        assert_eq!(result, Some(2.72));
863    }
864
865    #[test]
866    fn get_optional_float_at_path_returns_none_for_missing() {
867        let map: BTreeMap<String, Value> = BTreeMap::new();
868        let result = map.get_optional_float_at_path("f").unwrap();
869        assert_eq!(result, None);
870    }
871
872    #[test]
873    fn get_optional_float_at_path_returns_none_for_null() {
874        let map = map_with(vec![("f", Value::Null)]);
875        let result = map.get_optional_float_at_path("f").unwrap();
876        assert_eq!(result, None);
877    }
878
879    // -----------------------------------------------------------------------
880    // get_bool_at_path / get_optional_bool_at_path
881    // -----------------------------------------------------------------------
882
883    #[test]
884    fn get_bool_at_path_success() {
885        let map = map_with(vec![("flag", Value::Bool(true))]);
886        let result = map.get_bool_at_path("flag").unwrap();
887        assert!(result);
888    }
889
890    #[test]
891    fn get_bool_at_path_missing_errors() {
892        let map: BTreeMap<String, Value> = BTreeMap::new();
893        assert!(map.get_bool_at_path("flag").is_err());
894    }
895
896    #[test]
897    fn get_optional_bool_at_path_returns_some() {
898        let map = map_with(vec![("b", Value::Bool(false))]);
899        let result = map.get_optional_bool_at_path("b").unwrap();
900        assert_eq!(result, Some(false));
901    }
902
903    #[test]
904    fn get_optional_bool_at_path_returns_none_for_missing() {
905        let map: BTreeMap<String, Value> = BTreeMap::new();
906        let result = map.get_optional_bool_at_path("b").unwrap();
907        assert_eq!(result, None);
908    }
909
910    #[test]
911    fn get_optional_bool_at_path_returns_none_for_null() {
912        let map = map_with(vec![("b", Value::Null)]);
913        let result = map.get_optional_bool_at_path("b").unwrap();
914        assert_eq!(result, None);
915    }
916
917    // -----------------------------------------------------------------------
918    // get_inner_value_array_at_path / get_optional_inner_value_array_at_path
919    // -----------------------------------------------------------------------
920
921    #[test]
922    fn get_inner_value_array_at_path_success() {
923        let arr = Value::Array(vec![Value::U64(1), Value::U64(2), Value::U64(3)]);
924        let map = map_with(vec![("arr", arr)]);
925        let result: Vec<&Value> = map.get_inner_value_array_at_path("arr").unwrap();
926        assert_eq!(result.len(), 3);
927        assert_eq!(result[0], &Value::U64(1));
928    }
929
930    #[test]
931    fn get_inner_value_array_at_path_missing_errors() {
932        let map: BTreeMap<String, Value> = BTreeMap::new();
933        let result: Result<Vec<&Value>, Error> = map.get_inner_value_array_at_path("arr");
934        assert!(result.is_err());
935    }
936
937    #[test]
938    fn get_optional_inner_value_array_returns_some() {
939        let arr = Value::Array(vec![Value::Bool(true)]);
940        let map = map_with(vec![("arr", arr)]);
941        let result: Option<Vec<&Value>> =
942            map.get_optional_inner_value_array_at_path("arr").unwrap();
943        assert!(result.is_some());
944        assert_eq!(result.unwrap().len(), 1);
945    }
946
947    #[test]
948    fn get_optional_inner_value_array_returns_none_for_missing() {
949        let map: BTreeMap<String, Value> = BTreeMap::new();
950        let result: Option<Vec<&Value>> =
951            map.get_optional_inner_value_array_at_path("arr").unwrap();
952        assert_eq!(result, None);
953    }
954
955    // -----------------------------------------------------------------------
956    // get_inner_string_array_at_path / get_optional_inner_string_array_at_path
957    // -----------------------------------------------------------------------
958
959    #[test]
960    fn get_inner_string_array_at_path_success() {
961        let arr = Value::Array(vec![
962            Value::Text("a".to_string()),
963            Value::Text("b".to_string()),
964        ]);
965        let map = map_with(vec![("names", arr)]);
966        let result: Vec<String> = map.get_inner_string_array_at_path("names").unwrap();
967        assert_eq!(result, vec!["a".to_string(), "b".to_string()]);
968    }
969
970    #[test]
971    fn get_inner_string_array_at_path_missing_errors() {
972        let map: BTreeMap<String, Value> = BTreeMap::new();
973        let result: Result<Vec<String>, Error> = map.get_inner_string_array_at_path("names");
974        assert!(result.is_err());
975    }
976
977    #[test]
978    fn get_optional_inner_string_array_returns_some() {
979        let arr = Value::Array(vec![Value::Text("x".to_string())]);
980        let map = map_with(vec![("names", arr)]);
981        let result: Option<Vec<String>> = map
982            .get_optional_inner_string_array_at_path("names")
983            .unwrap();
984        assert_eq!(result, Some(vec!["x".to_string()]));
985    }
986
987    #[test]
988    fn get_optional_inner_string_array_returns_none_for_missing() {
989        let map: BTreeMap<String, Value> = BTreeMap::new();
990        let result: Option<Vec<String>> = map
991            .get_optional_inner_string_array_at_path("names")
992            .unwrap();
993        assert_eq!(result, None);
994    }
995
996    #[test]
997    fn get_optional_inner_string_array_errors_on_non_string_elements() {
998        let arr = Value::Array(vec![Value::U64(42)]);
999        let map = map_with(vec![("names", arr)]);
1000        let result: Result<Option<Vec<String>>, Error> =
1001            map.get_optional_inner_string_array_at_path("names");
1002        assert!(result.is_err());
1003    }
1004
1005    // -----------------------------------------------------------------------
1006    // get_optional_inner_borrowed_map_at_path
1007    // -----------------------------------------------------------------------
1008
1009    #[test]
1010    fn get_optional_inner_borrowed_map_at_path_returns_some() {
1011        let inner = nested_value_map(vec![("k", Value::U64(1))]);
1012        let map = map_with(vec![("m", inner)]);
1013        let result = map.get_optional_inner_borrowed_map_at_path("m").unwrap();
1014        assert!(result.is_some());
1015    }
1016
1017    #[test]
1018    fn get_optional_inner_borrowed_map_at_path_returns_none_for_missing() {
1019        let map: BTreeMap<String, Value> = BTreeMap::new();
1020        let result = map.get_optional_inner_borrowed_map_at_path("m").unwrap();
1021        assert_eq!(result, None);
1022    }
1023
1024    #[test]
1025    fn get_optional_inner_borrowed_map_at_path_errors_on_non_map() {
1026        let map = map_with(vec![("m", Value::U64(42))]);
1027        assert!(map.get_optional_inner_borrowed_map_at_path("m").is_err());
1028    }
1029
1030    // -----------------------------------------------------------------------
1031    // get_inner_borrowed_str_value_map_at_path /
1032    // get_optional_inner_borrowed_str_value_map_at_path
1033    // -----------------------------------------------------------------------
1034
1035    #[test]
1036    fn get_inner_borrowed_str_value_map_at_path_success() {
1037        let inner = nested_value_map(vec![("k", Value::Text("v".to_string()))]);
1038        let map = map_with(vec![("m", inner)]);
1039        let result: BTreeMap<String, &Value> =
1040            map.get_inner_borrowed_str_value_map_at_path("m").unwrap();
1041        assert_eq!(result.get("k"), Some(&&Value::Text("v".to_string())));
1042    }
1043
1044    #[test]
1045    fn get_inner_borrowed_str_value_map_at_path_missing_errors() {
1046        let map: BTreeMap<String, Value> = BTreeMap::new();
1047        let result: Result<BTreeMap<String, &Value>, Error> =
1048            map.get_inner_borrowed_str_value_map_at_path("m");
1049        assert!(result.is_err());
1050    }
1051
1052    #[test]
1053    fn get_optional_inner_borrowed_str_value_map_returns_some() {
1054        let inner = nested_value_map(vec![("a", Value::Bool(true))]);
1055        let map = map_with(vec![("m", inner)]);
1056        let result: Option<BTreeMap<String, &Value>> = map
1057            .get_optional_inner_borrowed_str_value_map_at_path("m")
1058            .unwrap();
1059        assert!(result.is_some());
1060    }
1061
1062    #[test]
1063    fn get_optional_inner_borrowed_str_value_map_returns_none_for_missing() {
1064        let map: BTreeMap<String, Value> = BTreeMap::new();
1065        let result: Option<BTreeMap<String, &Value>> = map
1066            .get_optional_inner_borrowed_str_value_map_at_path("m")
1067            .unwrap();
1068        assert_eq!(result, None);
1069    }
1070
1071    // -----------------------------------------------------------------------
1072    // get_hash256_bytes_at_path / get_optional_hash256_bytes_at_path
1073    // -----------------------------------------------------------------------
1074
1075    #[test]
1076    fn get_hash256_bytes_at_path_success() {
1077        let hash = [5u8; 32];
1078        let map = map_with(vec![("h", Value::Bytes32(hash))]);
1079        let result = map.get_hash256_bytes_at_path("h").unwrap();
1080        assert_eq!(result, hash);
1081    }
1082
1083    #[test]
1084    fn get_hash256_bytes_at_path_missing_errors() {
1085        let map: BTreeMap<String, Value> = BTreeMap::new();
1086        assert!(map.get_hash256_bytes_at_path("h").is_err());
1087    }
1088
1089    #[test]
1090    fn get_optional_hash256_bytes_at_path_returns_some() {
1091        let hash = [6u8; 32];
1092        let map = map_with(vec![("h", Value::Bytes32(hash))]);
1093        let result = map.get_optional_hash256_bytes_at_path("h").unwrap();
1094        assert_eq!(result, Some(hash));
1095    }
1096
1097    #[test]
1098    fn get_optional_hash256_bytes_at_path_returns_none() {
1099        let map: BTreeMap<String, Value> = BTreeMap::new();
1100        let result = map.get_optional_hash256_bytes_at_path("h").unwrap();
1101        assert_eq!(result, None);
1102    }
1103
1104    // -----------------------------------------------------------------------
1105    // get_bytes_at_path / get_optional_bytes_at_path
1106    // -----------------------------------------------------------------------
1107
1108    #[test]
1109    fn get_bytes_at_path_success() {
1110        let bytes = vec![1, 2, 3];
1111        let map = map_with(vec![("b", Value::Bytes(bytes.clone()))]);
1112        let result = map.get_bytes_at_path("b").unwrap();
1113        assert_eq!(result, bytes);
1114    }
1115
1116    #[test]
1117    fn get_bytes_at_path_missing_errors() {
1118        let map: BTreeMap<String, Value> = BTreeMap::new();
1119        assert!(map.get_bytes_at_path("b").is_err());
1120    }
1121
1122    #[test]
1123    fn get_optional_bytes_at_path_returns_some() {
1124        let bytes = vec![4, 5];
1125        let map = map_with(vec![("b", Value::Bytes(bytes.clone()))]);
1126        let result = map.get_optional_bytes_at_path("b").unwrap();
1127        assert_eq!(result, Some(bytes));
1128    }
1129
1130    #[test]
1131    fn get_optional_bytes_at_path_returns_none() {
1132        let map: BTreeMap<String, Value> = BTreeMap::new();
1133        let result = map.get_optional_bytes_at_path("b").unwrap();
1134        assert_eq!(result, None);
1135    }
1136
1137    // -----------------------------------------------------------------------
1138    // get_identifier_bytes_at_path / get_optional_identifier_bytes_at_path
1139    // -----------------------------------------------------------------------
1140
1141    #[test]
1142    fn get_identifier_bytes_at_path_success() {
1143        let id = [7u8; 32];
1144        let map = map_with(vec![("id", Value::Identifier(id))]);
1145        let result = map.get_identifier_bytes_at_path("id").unwrap();
1146        assert_eq!(result, id.to_vec());
1147    }
1148
1149    #[test]
1150    fn get_identifier_bytes_at_path_missing_errors() {
1151        let map: BTreeMap<String, Value> = BTreeMap::new();
1152        assert!(map.get_identifier_bytes_at_path("id").is_err());
1153    }
1154
1155    #[test]
1156    fn get_optional_identifier_bytes_at_path_returns_some() {
1157        let id = [8u8; 32];
1158        let map = map_with(vec![("id", Value::Identifier(id))]);
1159        let result = map.get_optional_identifier_bytes_at_path("id").unwrap();
1160        assert_eq!(result, Some(id.to_vec()));
1161    }
1162
1163    #[test]
1164    fn get_optional_identifier_bytes_at_path_returns_none() {
1165        let map: BTreeMap<String, Value> = BTreeMap::new();
1166        let result = map.get_optional_identifier_bytes_at_path("id").unwrap();
1167        assert_eq!(result, None);
1168    }
1169
1170    // -----------------------------------------------------------------------
1171    // get_binary_bytes_at_path / get_optional_binary_bytes_at_path
1172    // -----------------------------------------------------------------------
1173
1174    #[test]
1175    fn get_binary_bytes_at_path_success() {
1176        let bytes = vec![10, 20, 30];
1177        let map = map_with(vec![("bin", Value::Bytes(bytes.clone()))]);
1178        let result = map.get_binary_bytes_at_path("bin").unwrap();
1179        assert_eq!(result, bytes);
1180    }
1181
1182    #[test]
1183    fn get_binary_bytes_at_path_missing_errors() {
1184        let map: BTreeMap<String, Value> = BTreeMap::new();
1185        assert!(map.get_binary_bytes_at_path("bin").is_err());
1186    }
1187
1188    #[test]
1189    fn get_optional_binary_bytes_at_path_returns_some() {
1190        let bytes = vec![40, 50];
1191        let map = map_with(vec![("bin", Value::Bytes(bytes.clone()))]);
1192        let result = map.get_optional_binary_bytes_at_path("bin").unwrap();
1193        assert_eq!(result, Some(bytes));
1194    }
1195
1196    #[test]
1197    fn get_optional_binary_bytes_at_path_returns_none() {
1198        let map: BTreeMap<String, Value> = BTreeMap::new();
1199        let result = map.get_optional_binary_bytes_at_path("bin").unwrap();
1200        assert_eq!(result, None);
1201    }
1202
1203    // -----------------------------------------------------------------------
1204    // remove_string_at_path / remove_optional_string_at_path
1205    // -----------------------------------------------------------------------
1206
1207    #[test]
1208    fn remove_string_at_path_success() {
1209        let mut map = map_with(vec![("name", Value::Text("test".to_string()))]);
1210        let result = map.remove_string_at_path("name").unwrap();
1211        assert_eq!(result, "test");
1212        assert!(map.is_empty());
1213    }
1214
1215    #[test]
1216    fn remove_string_at_path_missing_errors() {
1217        let mut map: BTreeMap<String, Value> = BTreeMap::new();
1218        assert!(map.remove_string_at_path("name").is_err());
1219    }
1220
1221    #[test]
1222    fn remove_optional_string_at_path_returns_some() {
1223        let mut map = map_with(vec![("s", Value::Text("hi".to_string()))]);
1224        let result = map.remove_optional_string_at_path("s").unwrap();
1225        assert_eq!(result, Some("hi".to_string()));
1226    }
1227
1228    #[test]
1229    fn remove_optional_string_at_path_returns_none() {
1230        let mut map: BTreeMap<String, Value> = BTreeMap::new();
1231        let result = map.remove_optional_string_at_path("s").unwrap();
1232        assert_eq!(result, None);
1233    }
1234
1235    // -----------------------------------------------------------------------
1236    // remove_float_at_path / remove_optional_float_at_path
1237    // -----------------------------------------------------------------------
1238
1239    #[test]
1240    fn remove_float_at_path_success() {
1241        let mut map = map_with(vec![("f", Value::Float(1.5))]);
1242        let result = map.remove_float_at_path("f").unwrap();
1243        assert!((result - 1.5).abs() < f64::EPSILON);
1244    }
1245
1246    #[test]
1247    fn remove_float_at_path_missing_errors() {
1248        let mut map: BTreeMap<String, Value> = BTreeMap::new();
1249        assert!(map.remove_float_at_path("f").is_err());
1250    }
1251
1252    #[test]
1253    fn remove_optional_float_at_path_returns_some() {
1254        let mut map = map_with(vec![("f", Value::Float(0.5))]);
1255        let result = map.remove_optional_float_at_path("f").unwrap();
1256        assert_eq!(result, Some(0.5));
1257    }
1258
1259    #[test]
1260    fn remove_optional_float_at_path_returns_none_for_missing() {
1261        let mut map: BTreeMap<String, Value> = BTreeMap::new();
1262        let result = map.remove_optional_float_at_path("f").unwrap();
1263        assert_eq!(result, None);
1264    }
1265
1266    #[test]
1267    fn remove_optional_float_at_path_returns_none_for_null() {
1268        let mut map = map_with(vec![("f", Value::Null)]);
1269        let result = map.remove_optional_float_at_path("f").unwrap();
1270        assert_eq!(result, None);
1271    }
1272
1273    // -----------------------------------------------------------------------
1274    // remove_integer_at_path / remove_optional_integer_at_path
1275    // -----------------------------------------------------------------------
1276
1277    #[test]
1278    fn remove_integer_at_path_success() {
1279        let mut map = map_with(vec![("n", Value::U64(99))]);
1280        let result: u64 = map.remove_integer_at_path("n").unwrap();
1281        assert_eq!(result, 99);
1282    }
1283
1284    #[test]
1285    fn remove_integer_at_path_missing_errors() {
1286        let mut map: BTreeMap<String, Value> = BTreeMap::new();
1287        let result: Result<u64, Error> = map.remove_integer_at_path("n");
1288        assert!(result.is_err());
1289    }
1290
1291    #[test]
1292    fn remove_optional_integer_at_path_returns_some() {
1293        let mut map = map_with(vec![("n", Value::I32(-3))]);
1294        let result: Option<i32> = map.remove_optional_integer_at_path("n").unwrap();
1295        assert_eq!(result, Some(-3));
1296    }
1297
1298    #[test]
1299    fn remove_optional_integer_at_path_returns_none_for_missing() {
1300        let mut map: BTreeMap<String, Value> = BTreeMap::new();
1301        let result: Option<i32> = map.remove_optional_integer_at_path("n").unwrap();
1302        assert_eq!(result, None);
1303    }
1304
1305    #[test]
1306    fn remove_optional_integer_at_path_returns_none_for_null() {
1307        let mut map = map_with(vec![("n", Value::Null)]);
1308        let result: Option<i32> = map.remove_optional_integer_at_path("n").unwrap();
1309        assert_eq!(result, None);
1310    }
1311
1312    // -----------------------------------------------------------------------
1313    // remove_hash256_bytes_at_path / remove_optional_hash256_bytes_at_path
1314    // -----------------------------------------------------------------------
1315
1316    #[test]
1317    fn remove_hash256_bytes_at_path_success() {
1318        let hash = [9u8; 32];
1319        let mut map = map_with(vec![("h", Value::Bytes32(hash))]);
1320        let result = map.remove_hash256_bytes_at_path("h").unwrap();
1321        assert_eq!(result, hash);
1322        assert!(map.is_empty());
1323    }
1324
1325    #[test]
1326    fn remove_hash256_bytes_at_path_missing_errors() {
1327        let mut map: BTreeMap<String, Value> = BTreeMap::new();
1328        assert!(map.remove_hash256_bytes_at_path("h").is_err());
1329    }
1330
1331    #[test]
1332    fn remove_optional_hash256_bytes_at_path_returns_some() {
1333        let hash = [10u8; 32];
1334        let mut map = map_with(vec![("h", Value::Bytes32(hash))]);
1335        let result = map.remove_optional_hash256_bytes_at_path("h").unwrap();
1336        assert_eq!(result, Some(hash));
1337    }
1338
1339    #[test]
1340    fn remove_optional_hash256_bytes_at_path_returns_none() {
1341        let mut map: BTreeMap<String, Value> = BTreeMap::new();
1342        let result = map.remove_optional_hash256_bytes_at_path("h").unwrap();
1343        assert_eq!(result, None);
1344    }
1345
1346    // -----------------------------------------------------------------------
1347    // remove_identifier_at_path / remove_optional_identifier_at_path
1348    // -----------------------------------------------------------------------
1349
1350    #[test]
1351    fn remove_identifier_at_path_success() {
1352        let id = [11u8; 32];
1353        let mut map = map_with(vec![("id", Value::Identifier(id))]);
1354        let result = map.remove_identifier_at_path("id").unwrap();
1355        assert_eq!(result, Identifier::from(id));
1356        assert!(map.is_empty());
1357    }
1358
1359    #[test]
1360    fn remove_identifier_at_path_missing_errors() {
1361        let mut map: BTreeMap<String, Value> = BTreeMap::new();
1362        assert!(map.remove_identifier_at_path("id").is_err());
1363    }
1364
1365    #[test]
1366    fn remove_optional_identifier_at_path_returns_some() {
1367        let id = [12u8; 32];
1368        let mut map = map_with(vec![("id", Value::Identifier(id))]);
1369        let result = map.remove_optional_identifier_at_path("id").unwrap();
1370        assert_eq!(result, Some(Identifier::from(id)));
1371    }
1372
1373    #[test]
1374    fn remove_optional_identifier_at_path_returns_none() {
1375        let mut map: BTreeMap<String, Value> = BTreeMap::new();
1376        let result = map.remove_optional_identifier_at_path("id").unwrap();
1377        assert_eq!(result, None);
1378    }
1379
1380    // -----------------------------------------------------------------------
1381    // Nested path traversal edge cases
1382    // -----------------------------------------------------------------------
1383
1384    #[test]
1385    fn get_at_path_works_with_ref_values() {
1386        // Test that the impl works for BTreeMap<String, &Value> via the generic impl
1387        let source: BTreeMap<String, Value> =
1388            map_with(vec![("key", Value::Text("val".to_string()))]);
1389        let ref_map: BTreeMap<String, &Value> =
1390            source.iter().map(|(k, v)| (k.clone(), v)).collect();
1391        let result = ref_map.get_at_path("key").unwrap();
1392        assert_eq!(result, &Value::Text("val".to_string()));
1393    }
1394
1395    #[test]
1396    fn get_at_path_ref_map_nested() {
1397        let inner = nested_value_map(vec![("deep", Value::Bool(true))]);
1398        let source = map_with(vec![("top", inner)]);
1399        let ref_map: BTreeMap<String, &Value> =
1400            source.iter().map(|(k, v)| (k.clone(), v)).collect();
1401        let result = ref_map.get_at_path("top.deep").unwrap();
1402        assert_eq!(result, &Value::Bool(true));
1403    }
1404
1405    // -----------------------------------------------------------------------
1406    // Nested path: multi-level structure tests
1407    // -----------------------------------------------------------------------
1408
1409    #[test]
1410    fn get_string_at_nested_path() {
1411        let level2 = nested_value_map(vec![("name", Value::Text("deep".to_string()))]);
1412        let level1 = nested_value_map(vec![("child", level2)]);
1413        let map = map_with(vec![("parent", level1)]);
1414        let result = map.get_string_at_path("parent.child.name").unwrap();
1415        assert_eq!(result, "deep");
1416    }
1417
1418    #[test]
1419    fn get_bool_at_nested_path() {
1420        let inner = nested_value_map(vec![("enabled", Value::Bool(false))]);
1421        let map = map_with(vec![("config", inner)]);
1422        let result = map.get_bool_at_path("config.enabled").unwrap();
1423        assert!(!result);
1424    }
1425}