ploidy_pointer/
lib.rs

1use std::{
2    any::Any,
3    borrow::Cow,
4    collections::{BTreeMap, HashMap},
5    fmt::{Debug, Display},
6    ops::{Deref, Range},
7    rc::Rc,
8    sync::Arc,
9};
10
11use itertools::Itertools;
12
13#[cfg(feature = "derive")]
14pub use ploidy_pointer_derive::JsonPointee;
15
16/// A parsed JSON Pointer.
17#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
18pub struct JsonPointer<'a>(Cow<'a, [JsonPointerSegment<'a>]>);
19
20impl JsonPointer<'static> {
21    /// Constructs a pointer from an RFC 6901 string,
22    /// with segments that own their contents.
23    pub fn parse_owned(s: &str) -> Result<Self, BadJsonPointerSyntax> {
24        if s.is_empty() {
25            return Ok(Self::empty());
26        }
27        let Some(s) = s.strip_prefix('/') else {
28            return Err(BadJsonPointerSyntax::MissingLeadingSlash);
29        };
30        let segments = s
31            .split('/')
32            .map(str::to_owned)
33            .map(JsonPointerSegment::from_str)
34            .collect_vec();
35        Ok(Self(segments.into()))
36    }
37}
38
39impl<'a> JsonPointer<'a> {
40    /// Constructs an empty pointer that resolves to the current value.
41    pub fn empty() -> Self {
42        Self(Cow::Borrowed(&[]))
43    }
44
45    /// Constructs a pointer from an RFC 6901 string,
46    /// with segments that borrow from the string.
47    pub fn parse(s: &'a str) -> Result<Self, BadJsonPointerSyntax> {
48        if s.is_empty() {
49            return Ok(Self::empty());
50        }
51        let Some(s) = s.strip_prefix('/') else {
52            return Err(BadJsonPointerSyntax::MissingLeadingSlash);
53        };
54        let segments = s.split('/').map(JsonPointerSegment::from_str).collect_vec();
55        Ok(Self(segments.into()))
56    }
57
58    /// Returns `true` if this is an empty pointer.
59    pub fn is_empty(&self) -> bool {
60        self.0.is_empty()
61    }
62
63    /// Returns the first segment of this pointer, or `None`
64    /// if this is an empty pointer.
65    pub fn head(&self) -> Option<&JsonPointerSegment<'a>> {
66        self.0.first()
67    }
68
69    /// Returns a new pointer without the first segment of this pointer.
70    /// If this pointer has only one segment, or is an empty pointer,
71    /// returns an empty pointer.
72    pub fn tail(&self) -> JsonPointer<'_> {
73        self.0
74            .get(1..)
75            .map(|tail| JsonPointer(tail.into()))
76            .unwrap_or_else(JsonPointer::empty)
77    }
78
79    /// Returns a borrowing iterator over this pointer's segments.
80    pub fn segments(&self) -> JsonPointerSegments<'_> {
81        JsonPointerSegments(self.0.iter())
82    }
83
84    /// Returns a consuming iterator over this pointer's segments.
85    pub fn into_segments(self) -> IntoJsonPointerSegments<'a> {
86        IntoJsonPointerSegments(self.0.into_owned().into_iter())
87    }
88}
89
90impl Display for JsonPointer<'_> {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        match &*self.0 {
93            [] => Ok(()),
94            segments => write!(f, "/{}", segments.iter().format("/")),
95        }
96    }
97}
98
99/// A value that a [`JsonPointer`] points to.
100pub trait JsonPointee: Any {
101    /// Resolves a [`JsonPointer`] against this value.
102    fn resolve(&self, pointer: JsonPointer<'_>) -> Result<&dyn JsonPointee, BadJsonPointer>;
103}
104
105impl dyn JsonPointee {
106    /// Returns a reference to the pointed-to value if it's of type `T`,
107    /// or `None` if it isn't.
108    #[inline]
109    pub fn downcast_ref<T: JsonPointee>(&self) -> Option<&T> {
110        (self as &dyn Any).downcast_ref::<T>()
111    }
112
113    /// Returns `true` if the pointed-to value is of type `T`.
114    #[inline]
115    pub fn is<T: JsonPointee>(&self) -> bool {
116        (self as &dyn Any).is::<T>()
117    }
118}
119
120/// A borrowing iterator over the segments of a [`JsonPointer`].
121#[derive(Clone, Debug)]
122pub struct JsonPointerSegments<'a>(std::slice::Iter<'a, JsonPointerSegment<'a>>);
123
124impl<'a> Iterator for JsonPointerSegments<'a> {
125    type Item = &'a JsonPointerSegment<'a>;
126
127    #[inline]
128    fn next(&mut self) -> Option<Self::Item> {
129        self.0.next()
130    }
131
132    #[inline]
133    fn size_hint(&self) -> (usize, Option<usize>) {
134        self.0.size_hint()
135    }
136
137    #[inline]
138    fn count(self) -> usize {
139        self.0.count()
140    }
141
142    #[inline]
143    fn last(mut self) -> Option<Self::Item> {
144        self.next_back()
145    }
146}
147
148impl ExactSizeIterator for JsonPointerSegments<'_> {}
149
150impl DoubleEndedIterator for JsonPointerSegments<'_> {
151    #[inline]
152    fn next_back(&mut self) -> Option<Self::Item> {
153        self.0.next_back()
154    }
155}
156
157/// A consuming iterator over the segments of a [`JsonPointer`].
158#[derive(Debug)]
159pub struct IntoJsonPointerSegments<'a>(std::vec::IntoIter<JsonPointerSegment<'a>>);
160
161impl<'a> Iterator for IntoJsonPointerSegments<'a> {
162    type Item = JsonPointerSegment<'a>;
163
164    #[inline]
165    fn next(&mut self) -> Option<Self::Item> {
166        self.0.next()
167    }
168
169    #[inline]
170    fn size_hint(&self) -> (usize, Option<usize>) {
171        self.0.size_hint()
172    }
173
174    #[inline]
175    fn count(self) -> usize {
176        self.0.count()
177    }
178
179    #[inline]
180    fn last(mut self) -> Option<Self::Item> {
181        self.next_back()
182    }
183}
184
185impl ExactSizeIterator for IntoJsonPointerSegments<'_> {}
186
187impl DoubleEndedIterator for IntoJsonPointerSegments<'_> {
188    #[inline]
189    fn next_back(&mut self) -> Option<Self::Item> {
190        self.0.next_back()
191    }
192}
193
194/// A single segment of a [`JsonPointer`].
195#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
196pub struct JsonPointerSegment<'a>(Cow<'a, str>);
197
198impl<'a> JsonPointerSegment<'a> {
199    #[inline]
200    fn from_str(s: impl Into<Cow<'a, str>>) -> Self {
201        let s = s.into();
202        if s.contains('~') {
203            Self(s.replace("~1", "/").replace("~0", "~").into())
204        } else {
205            Self(s)
206        }
207    }
208
209    /// Returns the string value of this segment.
210    #[inline]
211    pub fn as_str(&self) -> &str {
212        self
213    }
214
215    /// Returns the value of this segment as an array index,
216    /// or `None` if this segment can't be used as an index.
217    #[inline]
218    pub fn to_index(&self) -> Option<usize> {
219        match self.as_bytes() {
220            [b'0'] => Some(0),
221            [b'1'..=b'9', rest @ ..] if rest.iter().all(|b: &u8| b.is_ascii_digit()) => {
222                // `usize::from_str` allows a leading `+`, and
223                // ignores leading zeros; RFC 6901 forbids both.
224                self.parse().ok()
225            }
226            _ => None,
227        }
228    }
229}
230
231impl Deref for JsonPointerSegment<'_> {
232    type Target = str;
233
234    #[inline]
235    fn deref(&self) -> &Self::Target {
236        &self.0
237    }
238}
239
240impl Display for JsonPointerSegment<'_> {
241    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242        write!(f, "{}", self.replace("~", "~0").replace("/", "~1"))
243    }
244}
245
246macro_rules! impl_pointee_for {
247    () => {};
248    (#[$($attrs:tt)+] $ty:ty $(, $($rest:tt)*)?) => {
249        #[$($attrs)*]
250        impl_pointee_for!($ty);
251        $(impl_pointee_for!($($rest)*);)?
252    };
253    ($ty:ty $(, $($rest:tt)*)?) => {
254        impl JsonPointee for $ty {
255            fn resolve(&self, pointer: JsonPointer<'_>) -> Result<&dyn JsonPointee, BadJsonPointer> {
256                if pointer.is_empty() {
257                    Ok(self)
258                } else {
259                    Err({
260                        #[cfg(feature = "did-you-mean")]
261                        let err = BadJsonPointerTy::with_ty(
262                            &pointer,
263                            JsonPointeeTy::Named(stringify!($ty)),
264                        );
265                        #[cfg(not(feature = "did-you-mean"))]
266                        let err = BadJsonPointerTy::new(&pointer);
267                        err
268                    })?
269                }
270            }
271        }
272        $(impl_pointee_for!($($rest)*);)?
273    };
274}
275
276impl_pointee_for!(
277    i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize, f32, f64, bool, String, &'static str,
278    #[cfg(feature = "chrono")] chrono::DateTime<chrono::Utc>,
279    #[cfg(feature = "url")] url::Url,
280);
281
282impl<T: JsonPointee> JsonPointee for Option<T> {
283    fn resolve(&self, pointer: JsonPointer<'_>) -> Result<&dyn JsonPointee, BadJsonPointer> {
284        if let Some(value) = self {
285            value.resolve(pointer)
286        } else {
287            let Some(key) = pointer.head() else {
288                return Ok(&None::<T>);
289            };
290            Err({
291                #[cfg(feature = "did-you-mean")]
292                let err = BadJsonPointerKey::with_ty(key, JsonPointeeTy::name_of(self));
293                #[cfg(not(feature = "did-you-mean"))]
294                let err = BadJsonPointerKey::new(key);
295                err
296            })?
297        }
298    }
299}
300
301impl<T: JsonPointee> JsonPointee for Box<T> {
302    fn resolve(&self, pointer: JsonPointer<'_>) -> Result<&dyn JsonPointee, BadJsonPointer> {
303        (**self).resolve(pointer)
304    }
305}
306
307impl<T: JsonPointee> JsonPointee for Arc<T> {
308    fn resolve(&self, pointer: JsonPointer<'_>) -> Result<&dyn JsonPointee, BadJsonPointer> {
309        (**self).resolve(pointer)
310    }
311}
312
313impl<T: JsonPointee> JsonPointee for Rc<T> {
314    fn resolve(&self, pointer: JsonPointer<'_>) -> Result<&dyn JsonPointee, BadJsonPointer> {
315        (**self).resolve(pointer)
316    }
317}
318
319impl<T: JsonPointee> JsonPointee for Vec<T> {
320    fn resolve(&self, pointer: JsonPointer<'_>) -> Result<&dyn JsonPointee, BadJsonPointer> {
321        let Some(key) = pointer.head() else {
322            return Ok(self);
323        };
324        if let Some(index) = key.to_index() {
325            if let Some(item) = self.get(index) {
326                item.resolve(pointer.tail())
327            } else {
328                Err(BadJsonPointer::Index(index, 0..self.len()))
329            }
330        } else {
331            Err({
332                #[cfg(feature = "did-you-mean")]
333                let err =
334                    BadJsonPointerTy::with_ty(&pointer, JsonPointeeTy::Named(stringify!($ty)));
335                #[cfg(not(feature = "did-you-mean"))]
336                let err = BadJsonPointerTy::new(&pointer);
337                err
338            })?
339        }
340    }
341}
342
343impl<T: JsonPointee> JsonPointee for HashMap<String, T> {
344    fn resolve(&self, pointer: JsonPointer<'_>) -> Result<&dyn JsonPointee, BadJsonPointer> {
345        let Some(key) = pointer.head() else {
346            return Ok(self);
347        };
348        if let Some(value) = self.get(key.as_str()) {
349            value.resolve(pointer.tail())
350        } else {
351            Err({
352                #[cfg(feature = "did-you-mean")]
353                let err = BadJsonPointerKey::with_suggestions(
354                    key,
355                    JsonPointeeTy::name_of(self),
356                    self.keys().map(|key| key.as_str()),
357                );
358                #[cfg(not(feature = "did-you-mean"))]
359                let err = BadJsonPointerKey::new(key);
360                err
361            })?
362        }
363    }
364}
365
366impl<T: JsonPointee> JsonPointee for BTreeMap<String, T> {
367    fn resolve(&self, pointer: JsonPointer<'_>) -> Result<&dyn JsonPointee, BadJsonPointer> {
368        let Some(key) = pointer.head() else {
369            return Ok(self);
370        };
371        if let Some(value) = self.get(key.as_str()) {
372            value.resolve(pointer.tail())
373        } else {
374            Err({
375                #[cfg(feature = "did-you-mean")]
376                let err = BadJsonPointerKey::with_suggestions(
377                    key,
378                    JsonPointeeTy::name_of(self),
379                    self.keys().map(|key| key.as_str()),
380                );
381                #[cfg(not(feature = "did-you-mean"))]
382                let err = BadJsonPointerKey::new(key);
383                err
384            })?
385        }
386    }
387}
388
389#[cfg(feature = "indexmap")]
390impl<T: JsonPointee> JsonPointee for indexmap::IndexMap<String, T> {
391    fn resolve(&self, pointer: JsonPointer<'_>) -> Result<&dyn JsonPointee, BadJsonPointer> {
392        let Some(key) = pointer.head() else {
393            return Ok(self);
394        };
395        if let Some(value) = self.get(key.as_str()) {
396            value.resolve(pointer.tail())
397        } else {
398            Err({
399                #[cfg(feature = "did-you-mean")]
400                let err = BadJsonPointerKey::with_suggestions(
401                    key,
402                    JsonPointeeTy::name_of(self),
403                    self.keys().map(|key| key.as_str()),
404                );
405                #[cfg(not(feature = "did-you-mean"))]
406                let err = BadJsonPointerKey::new(key);
407                err
408            })?
409        }
410    }
411}
412
413#[cfg(feature = "serde_json")]
414impl JsonPointee for serde_json::Value {
415    fn resolve(&self, pointer: JsonPointer<'_>) -> Result<&dyn JsonPointee, BadJsonPointer> {
416        let Some(key) = pointer.head() else {
417            return Ok(self);
418        };
419        match self {
420            serde_json::Value::Object(map) => {
421                if let Some(value) = map.get(key.as_str()) {
422                    value.resolve(pointer.tail())
423                } else {
424                    Err({
425                        #[cfg(feature = "did-you-mean")]
426                        let err = BadJsonPointerKey::with_suggestions(
427                            key,
428                            JsonPointeeTy::name_of(map),
429                            map.keys().map(|key| key.as_str()),
430                        );
431                        #[cfg(not(feature = "did-you-mean"))]
432                        let err = BadJsonPointerKey::new(key);
433                        err
434                    })?
435                }
436            }
437            serde_json::Value::Array(array) => {
438                let Some(index) = key.to_index() else {
439                    return Err({
440                        #[cfg(feature = "did-you-mean")]
441                        let err =
442                            BadJsonPointerTy::with_ty(&pointer, JsonPointeeTy::name_of(array));
443                        #[cfg(not(feature = "did-you-mean"))]
444                        let err = BadJsonPointerTy::new(&pointer);
445                        err
446                    })?;
447                };
448                if let Some(item) = array.get(index) {
449                    item.resolve(pointer.tail())
450                } else {
451                    Err(BadJsonPointer::Index(index, 0..array.len()))
452                }
453            }
454            serde_json::Value::Null => Err({
455                #[cfg(feature = "did-you-mean")]
456                let err = BadJsonPointerKey::with_ty(key, JsonPointeeTy::name_of(self));
457                #[cfg(not(feature = "did-you-mean"))]
458                let err = BadJsonPointerKey::new(key);
459                err
460            })?,
461            _ => Err({
462                #[cfg(feature = "did-you-mean")]
463                let err = BadJsonPointerTy::with_ty(&pointer, JsonPointeeTy::name_of(self));
464                #[cfg(not(feature = "did-you-mean"))]
465                let err = BadJsonPointerTy::new(&pointer);
466                err
467            })?,
468        }
469    }
470}
471
472/// An error that occurs during parsing.
473#[derive(Debug, thiserror::Error)]
474pub enum BadJsonPointerSyntax {
475    #[error("JSON Pointer must start with `/`")]
476    MissingLeadingSlash,
477}
478
479/// An error that occurs during traversal.
480#[derive(Debug, thiserror::Error)]
481pub enum BadJsonPointer {
482    #[error(transparent)]
483    Key(#[from] BadJsonPointerKey),
484    #[error("index {} out of range {}..{}", .0, .1.start, .1.end)]
485    Index(usize, Range<usize>),
486    #[error(transparent)]
487    Ty(#[from] BadJsonPointerTy),
488}
489
490/// An error that occurs when a pointed-to value doesn't have a key
491/// that the pointer references, with an optional suggestion
492/// for the correct key.
493#[derive(Debug)]
494pub struct BadJsonPointerKey {
495    pub key: String,
496    pub context: Option<BadJsonPointerKeyContext>,
497}
498
499impl BadJsonPointerKey {
500    #[cold]
501    pub fn new(key: &JsonPointerSegment<'_>) -> Self {
502        Self {
503            key: key.to_string(),
504            context: None,
505        }
506    }
507
508    #[cfg(feature = "did-you-mean")]
509    #[cold]
510    pub fn with_ty(key: &JsonPointerSegment<'_>, ty: JsonPointeeTy) -> Self {
511        Self {
512            key: key.to_string(),
513            context: Some(BadJsonPointerKeyContext {
514                ty,
515                suggestion: None,
516            }),
517        }
518    }
519
520    #[cfg(feature = "did-you-mean")]
521    #[cold]
522    pub fn with_suggestions<'a>(
523        key: &'a JsonPointerSegment<'_>,
524        ty: JsonPointeeTy,
525        suggestions: impl IntoIterator<Item = &'a str>,
526    ) -> Self {
527        let suggestion = suggestions
528            .into_iter()
529            .map(|suggestion| (suggestion, strsim::jaro_winkler(key.as_str(), suggestion)))
530            .max_by(|&(_, a), &(_, b)| {
531                // `strsim::jaro_winkler` returns the Jaro-Winkler _similarity_,
532                // not distance; so higher values mean the strings are closer.
533                a.partial_cmp(&b).unwrap_or(std::cmp::Ordering::Equal)
534            })
535            .map(|(suggestion, _)| suggestion.to_owned());
536        Self {
537            key: key.to_string(),
538            context: Some(BadJsonPointerKeyContext { ty, suggestion }),
539        }
540    }
541}
542
543impl std::error::Error for BadJsonPointerKey {}
544
545impl Display for BadJsonPointerKey {
546    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
547        match &self.context {
548            Some(BadJsonPointerKeyContext {
549                ty,
550                suggestion: Some(suggestion),
551            }) => write!(
552                f,
553                "unknown key {:?} for value of {ty}; did you mean {suggestion:?}?",
554                self.key
555            ),
556            Some(BadJsonPointerKeyContext {
557                ty,
558                suggestion: None,
559            }) => write!(f, "unknown key {:?} for value of {ty}", self.key),
560            None => write!(f, "unknown key {:?}", self.key),
561        }
562    }
563}
564
565#[derive(Debug)]
566pub struct BadJsonPointerKeyContext {
567    pub ty: JsonPointeeTy,
568    pub suggestion: Option<String>,
569}
570
571/// An error that occurs when a pointer can't be resolved
572/// against a value of the given type.
573#[derive(Debug)]
574pub struct BadJsonPointerTy {
575    pub pointer: String,
576    pub ty: Option<JsonPointeeTy>,
577}
578
579impl BadJsonPointerTy {
580    pub fn new(pointer: &JsonPointer<'_>) -> Self {
581        Self {
582            pointer: pointer.to_string(),
583            ty: None,
584        }
585    }
586
587    #[cfg(feature = "did-you-mean")]
588    #[cold]
589    pub fn with_ty(pointer: &JsonPointer<'_>, ty: JsonPointeeTy) -> Self {
590        Self {
591            pointer: pointer.to_string(),
592            ty: Some(ty),
593        }
594    }
595}
596
597impl std::error::Error for BadJsonPointerTy {}
598
599impl Display for BadJsonPointerTy {
600    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
601        match self.ty {
602            Some(ty) => write!(f, "can't resolve {:?} against value of {ty}", self.pointer),
603            None => write!(f, "can't resolve {:?}", self.pointer),
604        }
605    }
606}
607
608/// The name of a pointed-to type, for reporting traversal errors.
609#[derive(Clone, Copy, Debug, Eq, PartialEq)]
610pub enum JsonPointeeTy {
611    Struct(JsonPointeeStructTy),
612    Variant(&'static str, JsonPointeeStructTy),
613    Named(&'static str),
614}
615
616impl JsonPointeeTy {
617    #[inline]
618    pub fn struct_named(ty: &'static str) -> Self {
619        Self::Struct(JsonPointeeStructTy::Named(ty))
620    }
621
622    #[inline]
623    pub fn tuple_struct_named(ty: &'static str) -> Self {
624        Self::Struct(JsonPointeeStructTy::Tuple(ty))
625    }
626
627    #[inline]
628    pub fn unit_struct_named(ty: &'static str) -> Self {
629        Self::Struct(JsonPointeeStructTy::Unit(ty))
630    }
631
632    #[inline]
633    pub fn struct_variant_named(ty: &'static str, variant: &'static str) -> Self {
634        Self::Variant(ty, JsonPointeeStructTy::Named(variant))
635    }
636
637    #[inline]
638    pub fn tuple_variant_named(ty: &'static str, variant: &'static str) -> Self {
639        Self::Variant(ty, JsonPointeeStructTy::Tuple(variant))
640    }
641
642    #[inline]
643    pub fn unit_variant_named(ty: &'static str, variant: &'static str) -> Self {
644        Self::Variant(ty, JsonPointeeStructTy::Unit(variant))
645    }
646
647    #[inline]
648    pub fn named<T: ?Sized>() -> Self {
649        Self::Named(std::any::type_name::<T>())
650    }
651
652    #[inline]
653    pub fn name_of<T: ?Sized>(value: &T) -> Self {
654        Self::Named(std::any::type_name_of_val(value))
655    }
656}
657
658impl Display for JsonPointeeTy {
659    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
660        match self {
661            Self::Struct(JsonPointeeStructTy::Named(ty)) => write!(f, "struct `{ty}`"),
662            Self::Struct(JsonPointeeStructTy::Tuple(ty)) => write!(f, "tuple struct `{ty}`"),
663            Self::Struct(JsonPointeeStructTy::Unit(ty)) => write!(f, "unit struct `{ty}`"),
664            Self::Variant(ty, JsonPointeeStructTy::Named(variant)) => {
665                write!(f, "variant `{variant}` of `{ty}`")
666            }
667            Self::Variant(ty, JsonPointeeStructTy::Tuple(variant)) => {
668                write!(f, "tuple variant `{variant}` of `{ty}`")
669            }
670            Self::Variant(ty, JsonPointeeStructTy::Unit(variant)) => {
671                write!(f, "unit variant `{variant}` of `{ty}`")
672            }
673            Self::Named(ty) => write!(f, "type `{ty}`"),
674        }
675    }
676}
677
678/// The name of a pointed-to struct type or enum variant,
679/// for reporting traversal errors.
680#[derive(Clone, Copy, Debug, Eq, PartialEq)]
681pub enum JsonPointeeStructTy {
682    Named(&'static str),
683    Tuple(&'static str),
684    Unit(&'static str),
685}
686
687#[cfg(test)]
688mod tests {
689    use super::*;
690
691    #[test]
692    fn test_parse_pointer() {
693        let pointer = JsonPointer::parse("/foo/bar/0").unwrap();
694        let mut segments = pointer.into_segments();
695        assert_eq!(segments.len(), 3);
696        assert_eq!(segments.next(), Some(JsonPointerSegment::from_str("foo")));
697        assert_eq!(segments.next(), Some(JsonPointerSegment::from_str("bar")));
698        // `"0"` is parsed as a string segment, but implementations for `Vec`
699        // and tuple structs will parse it as an index.
700        assert_eq!(segments.next(), Some(JsonPointerSegment::from_str("0")));
701        assert_eq!(segments.next(), None);
702    }
703
704    #[test]
705    fn test_parse_pointer_escaping() {
706        let pointer = JsonPointer::parse("/foo~1bar/baz~0qux").unwrap();
707        let mut segments = pointer.into_segments();
708        assert_eq!(segments.len(), 2);
709        assert_eq!(
710            segments.next(),
711            Some(JsonPointerSegment::from_str("foo~1bar"))
712        );
713        assert_eq!(
714            segments.next(),
715            Some(JsonPointerSegment::from_str("baz~0qux"))
716        );
717        assert_eq!(segments.next(), None);
718    }
719
720    #[test]
721    fn test_resolve_vec() {
722        let data = vec![1, 2, 3];
723        let pointer = JsonPointer::parse("/1").unwrap();
724        let result = data.resolve(pointer).unwrap();
725        assert_eq!(result.downcast_ref::<i32>(), Some(&2));
726    }
727
728    #[test]
729    fn test_resolve_hashmap() {
730        let mut data = HashMap::new();
731        data.insert("foo".to_string(), 42);
732
733        let pointer = JsonPointer::parse("/foo").unwrap();
734        let result = data.resolve(pointer).unwrap();
735        assert_eq!(result.downcast_ref::<i32>(), Some(&42));
736    }
737
738    #[test]
739    fn test_resolve_option() {
740        let data = Some(42);
741        let pointer = JsonPointer::parse("").unwrap();
742        let result = data.resolve(pointer).unwrap();
743        assert_eq!(result.downcast_ref::<i32>(), Some(&42));
744    }
745
746    #[test]
747    fn test_primitive_empty_path() {
748        let data = 42;
749        let pointer = JsonPointer::parse("").unwrap();
750        let result = data.resolve(pointer).unwrap();
751        assert_eq!(result.downcast_ref::<i32>(), Some(&42));
752    }
753
754    #[test]
755    fn test_primitive_non_empty_path() {
756        let data = 42;
757        let pointer = JsonPointer::parse("/foo").unwrap();
758        assert!(data.resolve(pointer).is_err());
759    }
760
761    #[test]
762    fn test_segments() {
763        let pointer = JsonPointer::parse("/foo/bar/baz").unwrap();
764
765        // Can iterate multiple times with borrowing iterator.
766        let segments: Vec<_> = pointer.segments().map(|s| s.as_str()).collect();
767        assert_eq!(segments, vec!["foo", "bar", "baz"]);
768
769        // Pointer is still usable after borrowing iteration.
770        let segments_again: Vec<_> = pointer.segments().map(|s| s.as_str()).collect();
771        assert_eq!(segments_again, vec!["foo", "bar", "baz"]);
772
773        // Verify iterator traits.
774        assert_eq!(pointer.segments().len(), 3);
775        assert_eq!(pointer.segments().last().map(|s| s.as_str()), Some("baz"));
776        assert_eq!(
777            pointer.segments().next_back().map(|s| s.as_str()),
778            Some("baz")
779        );
780    }
781}