ploidy_pointer/
lib.rs

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