Skip to main content

ploidy_pointer/
lib.rs

1use std::{
2    any::Any,
3    borrow::{Borrow, Cow},
4    collections::{BTreeMap, HashMap},
5    fmt::{Debug, Display},
6    hash::BuildHasher,
7    iter::FusedIterator,
8    ops::{Deref, Range},
9    rc::Rc,
10    str::Split,
11    sync::Arc,
12};
13
14use ref_cast::{RefCastCustom, ref_cast_custom};
15
16#[cfg(feature = "derive")]
17pub use ploidy_pointer_derive::{JsonPointee, JsonPointerTarget};
18
19/// A JSON Pointer.
20#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd, RefCastCustom)]
21#[repr(transparent)]
22pub struct JsonPointer(str);
23
24impl JsonPointer {
25    #[ref_cast_custom]
26    fn new(raw: &str) -> &Self;
27
28    /// Parses a pointer from an RFC 6901 string.
29    ///
30    /// The empty string is the valid root pointer.
31    /// All other strings must start with `/`.
32    #[inline]
33    pub fn parse(s: &str) -> Result<&Self, JsonPointerSyntaxError> {
34        if s.is_empty() || s.starts_with('/') {
35            Ok(Self::new(s))
36        } else {
37            Err(JsonPointerSyntaxError)
38        }
39    }
40
41    /// Returns the empty root pointer.
42    #[inline]
43    pub fn empty() -> &'static Self {
44        JsonPointer::new("")
45    }
46
47    /// Returns `true` if this is the empty root pointer.
48    #[inline]
49    pub fn is_empty(&self) -> bool {
50        self.0.is_empty()
51    }
52
53    /// Returns the first segment, or `None` for the root pointer.
54    #[inline]
55    pub fn head(&self) -> Option<&JsonPointerSegment> {
56        let rest = self.0.strip_prefix('/')?;
57        let raw = rest.find('/').map(|index| &rest[..index]).unwrap_or(rest);
58        Some(JsonPointerSegment::new(raw))
59    }
60
61    /// Returns the pointer without its first segment.
62    ///
63    /// For the root pointer, returns the root pointer.
64    #[inline]
65    pub fn tail(&self) -> &JsonPointer {
66        self.0
67            .strip_prefix('/')
68            .and_then(|rest| rest.find('/').map(|index| &rest[index..]))
69            .map(JsonPointer::new)
70            .unwrap_or_else(|| JsonPointer::empty())
71    }
72
73    /// Returns a borrowing iterator over the segments.
74    #[inline]
75    pub fn segments(&self) -> JsonPointerSegments<'_> {
76        JsonPointerSegments(self.0.strip_prefix('/').map(|raw| raw.split('/')))
77    }
78
79    /// Follows this pointer through `root` and extracts the result as `T`.
80    #[inline]
81    pub fn follow<'a, T: JsonPointerTarget<'a>>(
82        &self,
83        root: &'a (impl JsonPointee + ?Sized),
84    ) -> Result<T, JsonPointerError> {
85        T::from_pointee(root.resolve(self)?).map_err(|err| JsonPointerError::Type {
86            pointer: self.to_owned(),
87            source: err,
88        })
89    }
90}
91
92impl Display for JsonPointer {
93    #[inline]
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        f.write_str(&self.0)
96    }
97}
98
99impl<'a> From<&'a JsonPointer> for Cow<'a, JsonPointer> {
100    #[inline]
101    fn from(value: &'a JsonPointer) -> Self {
102        Cow::Borrowed(value)
103    }
104}
105
106impl ToOwned for JsonPointer {
107    type Owned = JsonPointerBuf;
108
109    #[inline]
110    fn to_owned(&self) -> Self::Owned {
111        JsonPointerBuf(self.0.to_owned())
112    }
113}
114
115/// An owned JSON Pointer.
116#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
117pub struct JsonPointerBuf(String);
118
119impl JsonPointerBuf {
120    /// Parses an owned pointer from an RFC 6901 string.
121    ///
122    /// The empty string is the valid root pointer.
123    /// All other strings must start with `/`.
124    #[inline]
125    pub fn parse(s: String) -> Result<Self, JsonPointerSyntaxError> {
126        if s.is_empty() || s.starts_with('/') {
127            Ok(Self(s))
128        } else {
129            Err(JsonPointerSyntaxError)
130        }
131    }
132}
133
134impl AsRef<JsonPointer> for JsonPointerBuf {
135    #[inline]
136    fn as_ref(&self) -> &JsonPointer {
137        self
138    }
139}
140
141impl Borrow<JsonPointer> for JsonPointerBuf {
142    #[inline]
143    fn borrow(&self) -> &JsonPointer {
144        self
145    }
146}
147
148impl Deref for JsonPointerBuf {
149    type Target = JsonPointer;
150
151    #[inline]
152    fn deref(&self) -> &Self::Target {
153        JsonPointer::new(&self.0)
154    }
155}
156
157impl Display for JsonPointerBuf {
158    #[inline]
159    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160        <JsonPointer as Display>::fmt(self, f)
161    }
162}
163
164impl From<JsonPointerBuf> for Cow<'_, JsonPointer> {
165    #[inline]
166    fn from(value: JsonPointerBuf) -> Self {
167        Cow::Owned(value)
168    }
169}
170
171impl From<&JsonPointer> for JsonPointerBuf {
172    #[inline]
173    fn from(value: &JsonPointer) -> Self {
174        value.to_owned()
175    }
176}
177
178/// A value that a [`JsonPointer`] points to.
179pub trait JsonPointee: Any {
180    /// Resolves a [`JsonPointer`] against this value.
181    fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError>;
182
183    /// Returns the concrete type name of this value.
184    #[inline]
185    fn name(&self) -> &'static str {
186        std::any::type_name::<Self>()
187    }
188}
189
190/// Extracts a typed value from a [`JsonPointee`].
191pub trait JsonPointerTarget<'a>: Sized {
192    /// Tries to extract `Self` from a resolved pointee.
193    fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError>;
194}
195
196/// Convenience methods for [`JsonPointee`] types.
197pub trait JsonPointeeExt: JsonPointee {
198    /// Parses a JSON pointer string, resolves it against this value,
199    /// and extracts the result as `T`.
200    #[inline]
201    fn pointer<'a, T: JsonPointerTarget<'a>>(&'a self, path: &str) -> Result<T, JsonPointerError> {
202        JsonPointer::parse(path)?.follow(self)
203    }
204}
205
206impl<P: JsonPointee + ?Sized> JsonPointeeExt for P {}
207
208/// A single segment of a [`JsonPointer`].
209#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RefCastCustom)]
210#[repr(transparent)]
211pub struct JsonPointerSegment(str);
212
213impl JsonPointerSegment {
214    #[ref_cast_custom]
215    fn new(raw: &str) -> &Self;
216
217    /// Returns the value of this segment as a string.
218    #[inline]
219    pub fn to_str(&self) -> Cow<'_, str> {
220        if self.0.contains('~') {
221            self.0.replace("~1", "/").replace("~0", "~").into()
222        } else {
223            Cow::Borrowed(&self.0)
224        }
225    }
226
227    /// Returns the value of this segment as an array index,
228    /// or `None` if this segment can't be used as an index.
229    #[inline]
230    pub fn to_index(&self) -> Option<usize> {
231        match self.0.as_bytes() {
232            [b'0'] => Some(0),
233            [b'1'..=b'9', rest @ ..] if rest.iter().all(u8::is_ascii_digit) => {
234                // `usize::from_str` allows a leading `+`, and
235                // ignores leading zeros; RFC 6901 forbids both.
236                self.0.parse().ok()
237            }
238            _ => None,
239        }
240    }
241
242    /// Returns `true` if this segment is empty.
243    #[inline]
244    pub fn is_empty(&self) -> bool {
245        self.0.is_empty()
246    }
247}
248
249impl PartialEq<str> for JsonPointerSegment {
250    #[inline]
251    fn eq(&self, other: &str) -> bool {
252        self.to_str() == other
253    }
254}
255
256impl PartialEq<JsonPointerSegment> for str {
257    #[inline]
258    fn eq(&self, other: &JsonPointerSegment) -> bool {
259        other == self
260    }
261}
262
263impl Display for JsonPointerSegment {
264    #[inline]
265    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
266        f.write_str(&self.to_str())
267    }
268}
269
270/// A borrowing iterator over the segments of a [`JsonPointer`].
271#[derive(Clone, Debug)]
272pub struct JsonPointerSegments<'a>(Option<Split<'a, char>>);
273
274impl<'a> Iterator for JsonPointerSegments<'a> {
275    type Item = &'a JsonPointerSegment;
276
277    #[inline]
278    fn next(&mut self) -> Option<Self::Item> {
279        self.0
280            .as_mut()
281            .and_then(|iter| iter.next())
282            .map(JsonPointerSegment::new)
283    }
284}
285
286impl<'a> DoubleEndedIterator for JsonPointerSegments<'a> {
287    #[inline]
288    fn next_back(&mut self) -> Option<Self::Item> {
289        self.0
290            .as_mut()
291            .and_then(|iter| iter.next_back())
292            .map(JsonPointerSegment::new)
293    }
294}
295
296impl FusedIterator for JsonPointerSegments<'_> {}
297
298macro_rules! impl_pointee_for {
299    () => {};
300    (#[$($attrs:tt)+] $ty:ty $(, $($rest:tt)*)?) => {
301        #[$($attrs)*]
302        impl_pointee_for!($ty);
303        $(impl_pointee_for!($($rest)*);)?
304    };
305    ($ty:ty $(, $($rest:tt)*)?) => {
306        impl JsonPointee for $ty {
307            fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
308                if pointer.is_empty() {
309                    Ok(self)
310                } else {
311                    Err({
312                        #[cfg(feature = "did-you-mean")]
313                        let err = JsonPointerTypeError::with_ty(
314                            pointer,
315                            JsonPointeeType::Named(stringify!($ty)),
316                        );
317                        #[cfg(not(feature = "did-you-mean"))]
318                        let err = JsonPointerTypeError::new(pointer);
319                        err
320                    })?
321                }
322            }
323        }
324        $(impl_pointee_for!($($rest)*);)?
325    };
326}
327
328impl_pointee_for!(
329    i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize, f32, f64, bool, String, &'static str,
330    #[cfg(feature = "chrono")] chrono::DateTime<chrono::Utc>,
331    #[cfg(feature = "chrono")] chrono::NaiveDate,
332    #[cfg(feature = "url")] url::Url,
333    #[cfg(feature = "uuid")] uuid::Uuid,
334    #[cfg(feature = "serde_bytes")] serde_bytes::ByteBuf,
335);
336
337macro_rules! impl_copied_pointer_target_for {
338    () => {};
339    (#[$($attrs:tt)+] $ty:ty $(, $($rest:tt)*)?) => {
340        #[$($attrs)*]
341        impl_copied_pointer_target_for!($ty);
342        $(impl_copied_pointer_target_for!($($rest)*);)?
343    };
344    ($ty:ty $(, $($rest:tt)*)?) => {
345        impl<'a> JsonPointerTarget<'a> for $ty {
346            #[inline]
347            fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
348                let any: &dyn Any = pointee;
349                any.downcast_ref::<$ty>().copied().ok_or_else(|| JsonPointerTargetError {
350                    expected: ::std::any::type_name::<$ty>(),
351                    actual: pointee.name(),
352                })
353            }
354        }
355        $(impl_copied_pointer_target_for!($($rest)*);)?
356    };
357}
358
359impl_copied_pointer_target_for!(
360    i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize, f32, f64, bool,
361    #[cfg(feature = "chrono")] chrono::DateTime<chrono::Utc>,
362    #[cfg(feature = "chrono")] chrono::NaiveDate,
363    #[cfg(feature = "uuid")] uuid::Uuid,
364);
365
366#[cfg(feature = "serde_bytes")]
367impl<'a> JsonPointerTarget<'a> for &'a serde_bytes::ByteBuf {
368    #[inline]
369    fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
370        let any: &dyn Any = pointee;
371        any.downcast_ref::<serde_bytes::ByteBuf>()
372            .ok_or_else(|| JsonPointerTargetError {
373                expected: ::std::any::type_name::<serde_bytes::ByteBuf>(),
374                actual: pointee.name(),
375            })
376    }
377}
378
379impl<'a> JsonPointerTarget<'a> for &'a str {
380    #[inline]
381    fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
382        let any: &dyn Any = pointee;
383        if let Some(s) = any.downcast_ref::<String>() {
384            Ok(s.as_str())
385        } else if let Some(&s) = any.downcast_ref::<&str>() {
386            Ok(s)
387        } else {
388            Err(JsonPointerTargetError {
389                expected: "str",
390                actual: pointee.name(),
391            })
392        }
393    }
394}
395
396impl<'a, T: JsonPointee> JsonPointerTarget<'a> for &'a [T] {
397    #[inline]
398    fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
399        let any: &dyn Any = pointee;
400        if let Some(v) = any.downcast_ref::<Vec<T>>() {
401            Ok(v.as_slice())
402        } else if let Some(v) = any.downcast_ref::<&[T]>() {
403            Ok(v)
404        } else {
405            Err(JsonPointerTargetError {
406                expected: ::std::any::type_name::<[T]>(),
407                actual: pointee.name(),
408            })
409        }
410    }
411}
412
413#[cfg(feature = "url")]
414impl<'a> JsonPointerTarget<'a> for &'a url::Url {
415    #[inline]
416    fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
417        let any: &dyn Any = pointee;
418        any.downcast_ref::<url::Url>()
419            .ok_or_else(|| JsonPointerTargetError {
420                expected: ::std::any::type_name::<url::Url>(),
421                actual: pointee.name(),
422            })
423    }
424}
425
426impl<'a, T: JsonPointee, H: BuildHasher + 'static> JsonPointerTarget<'a>
427    for &'a HashMap<String, T, H>
428{
429    #[inline]
430    fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
431        let any: &dyn Any = pointee;
432        any.downcast_ref::<HashMap<String, T, H>>()
433            .ok_or_else(|| JsonPointerTargetError {
434                expected: ::std::any::type_name::<HashMap<String, T, H>>(),
435                actual: pointee.name(),
436            })
437    }
438}
439
440impl<'a, T: JsonPointee> JsonPointerTarget<'a> for &'a BTreeMap<String, T> {
441    #[inline]
442    fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
443        let any: &dyn Any = pointee;
444        any.downcast_ref::<BTreeMap<String, T>>()
445            .ok_or_else(|| JsonPointerTargetError {
446                expected: ::std::any::type_name::<BTreeMap<String, T>>(),
447                actual: pointee.name(),
448            })
449    }
450}
451
452#[cfg(feature = "indexmap")]
453impl<'a, T: JsonPointee, H: BuildHasher + 'static> JsonPointerTarget<'a>
454    for &'a indexmap::IndexMap<String, T, H>
455{
456    #[inline]
457    fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
458        let any: &dyn Any = pointee;
459        any.downcast_ref::<indexmap::IndexMap<String, T, H>>()
460            .ok_or_else(|| JsonPointerTargetError {
461                expected: ::std::any::type_name::<indexmap::IndexMap<String, T, H>>(),
462                actual: pointee.name(),
463            })
464    }
465}
466
467#[cfg(feature = "serde")]
468impl<'a> JsonPointerTarget<'a> for &'a serde::de::IgnoredAny {
469    #[inline]
470    fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
471        Err(JsonPointerTargetError {
472            expected: ::std::any::type_name::<serde::de::IgnoredAny>(),
473            actual: pointee.name(),
474        })
475    }
476}
477
478#[cfg(feature = "serde_json")]
479impl<'a> JsonPointerTarget<'a> for &'a serde_json::Value {
480    #[inline]
481    fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
482        let any: &dyn Any = pointee;
483        any.downcast_ref::<serde_json::Value>()
484            .ok_or_else(|| JsonPointerTargetError {
485                expected: ::std::any::type_name::<serde_json::Value>(),
486                actual: pointee.name(),
487            })
488    }
489}
490
491/// Transparently resolves a [`JsonPointer`] against the contained value
492/// if [`Some`], or returns an error if [`None`].
493impl<T: JsonPointee> JsonPointee for Option<T> {
494    fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
495        match self {
496            Some(value) => value.resolve(pointer),
497            None => Err({
498                #[cfg(feature = "did-you-mean")]
499                let err = JsonPointerTypeError::with_ty(pointer, JsonPointeeType::name_of(self));
500                #[cfg(not(feature = "did-you-mean"))]
501                let err = JsonPointerTypeError::new(pointer);
502                err
503            })?,
504        }
505    }
506}
507
508impl<T: JsonPointee> JsonPointee for Box<T> {
509    fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
510        (**self).resolve(pointer)
511    }
512}
513
514impl<T: JsonPointee> JsonPointee for Arc<T> {
515    fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
516        (**self).resolve(pointer)
517    }
518}
519
520impl<T: JsonPointee> JsonPointee for Rc<T> {
521    fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
522        (**self).resolve(pointer)
523    }
524}
525
526impl<T: JsonPointee> JsonPointee for Vec<T> {
527    fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
528        let Some(key) = pointer.head() else {
529            return Ok(self);
530        };
531        if let Some(index) = key.to_index() {
532            if let Some(item) = self.get(index) {
533                item.resolve(pointer.tail())
534            } else {
535                Err(JsonPointeeError::Index(index, 0..self.len()))
536            }
537        } else {
538            Err({
539                #[cfg(feature = "did-you-mean")]
540                let err = JsonPointerTypeError::with_ty(pointer, JsonPointeeType::name_of(self));
541                #[cfg(not(feature = "did-you-mean"))]
542                let err = JsonPointerTypeError::new(pointer);
543                err
544            })?
545        }
546    }
547}
548
549impl<T, H> JsonPointee for HashMap<String, T, H>
550where
551    T: JsonPointee,
552    H: BuildHasher + 'static,
553{
554    fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
555        let Some(key) = pointer.head() else {
556            return Ok(self);
557        };
558        if let Some(value) = self.get(&*key.to_str()) {
559            value.resolve(pointer.tail())
560        } else {
561            Err({
562                #[cfg(feature = "did-you-mean")]
563                let err = JsonPointerKeyError::with_suggestions(
564                    key,
565                    JsonPointeeType::name_of(self),
566                    self.keys().map(|key| key.as_str()),
567                );
568                #[cfg(not(feature = "did-you-mean"))]
569                let err = JsonPointerKeyError::new(key);
570                err
571            })?
572        }
573    }
574}
575
576impl<T: JsonPointee> JsonPointee for BTreeMap<String, T> {
577    fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
578        let Some(key) = pointer.head() else {
579            return Ok(self);
580        };
581        if let Some(value) = self.get(&*key.to_str()) {
582            value.resolve(pointer.tail())
583        } else {
584            Err({
585                #[cfg(feature = "did-you-mean")]
586                let err = JsonPointerKeyError::with_suggestions(
587                    key,
588                    JsonPointeeType::name_of(self),
589                    self.keys().map(|key| key.as_str()),
590                );
591                #[cfg(not(feature = "did-you-mean"))]
592                let err = JsonPointerKeyError::new(key);
593                err
594            })?
595        }
596    }
597}
598
599#[cfg(feature = "indexmap")]
600impl<T, H> JsonPointee for indexmap::IndexMap<String, T, H>
601where
602    T: JsonPointee,
603    H: BuildHasher + 'static,
604{
605    fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
606        let Some(key) = pointer.head() else {
607            return Ok(self);
608        };
609        if let Some(value) = self.get(&*key.to_str()) {
610            value.resolve(pointer.tail())
611        } else {
612            Err({
613                #[cfg(feature = "did-you-mean")]
614                let err = JsonPointerKeyError::with_suggestions(
615                    key,
616                    JsonPointeeType::name_of(self),
617                    self.keys().map(|key| key.as_str()),
618                );
619                #[cfg(not(feature = "did-you-mean"))]
620                let err = JsonPointerKeyError::new(key);
621                err
622            })?
623        }
624    }
625}
626
627#[cfg(feature = "serde")]
628impl JsonPointee for serde::de::IgnoredAny {
629    fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
630        Err({
631            #[cfg(feature = "did-you-mean")]
632            let err = JsonPointerTypeError::with_ty(pointer, JsonPointeeType::name_of(self));
633            #[cfg(not(feature = "did-you-mean"))]
634            let err = JsonPointerTypeError::new(pointer);
635            err
636        })?
637    }
638}
639
640#[cfg(feature = "serde_json")]
641impl JsonPointee for serde_json::Value {
642    fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
643        let Some(key) = pointer.head() else {
644            return Ok(self);
645        };
646        match self {
647            serde_json::Value::Object(map) => {
648                if let Some(value) = map.get(&*key.to_str()) {
649                    value.resolve(pointer.tail())
650                } else {
651                    Err({
652                        #[cfg(feature = "did-you-mean")]
653                        let err = JsonPointerKeyError::with_suggestions(
654                            key,
655                            JsonPointeeType::name_of(map),
656                            map.keys().map(|key| key.as_str()),
657                        );
658                        #[cfg(not(feature = "did-you-mean"))]
659                        let err = JsonPointerKeyError::new(key);
660                        err
661                    })?
662                }
663            }
664            serde_json::Value::Array(array) => {
665                let Some(index) = key.to_index() else {
666                    return Err({
667                        #[cfg(feature = "did-you-mean")]
668                        let err =
669                            JsonPointerTypeError::with_ty(pointer, JsonPointeeType::name_of(array));
670                        #[cfg(not(feature = "did-you-mean"))]
671                        let err = JsonPointerTypeError::new(pointer);
672                        err
673                    })?;
674                };
675                if let Some(item) = array.get(index) {
676                    item.resolve(pointer.tail())
677                } else {
678                    Err(JsonPointeeError::Index(index, 0..array.len()))
679                }
680            }
681            serde_json::Value::Null => Err({
682                #[cfg(feature = "did-you-mean")]
683                let err = JsonPointerKeyError::with_ty(key, JsonPointeeType::name_of(self));
684                #[cfg(not(feature = "did-you-mean"))]
685                let err = JsonPointerKeyError::new(key);
686                err
687            })?,
688            _ => Err({
689                #[cfg(feature = "did-you-mean")]
690                let err = JsonPointerTypeError::with_ty(pointer, JsonPointeeType::name_of(self));
691                #[cfg(not(feature = "did-you-mean"))]
692                let err = JsonPointerTypeError::new(pointer);
693                err
694            })?,
695        }
696    }
697}
698
699/// An error that occurs during pointer resolution.
700#[derive(Debug, thiserror::Error)]
701pub enum JsonPointerError {
702    #[error(transparent)]
703    Syntax(#[from] JsonPointerSyntaxError),
704    #[error(transparent)]
705    Resolve(#[from] JsonPointeeError),
706    #[error("at `{pointer}`: {source}")]
707    Type {
708        pointer: JsonPointerBuf,
709        #[source]
710        source: JsonPointerTargetError,
711    },
712}
713
714/// An error that occurs during parsing.
715#[derive(Debug, thiserror::Error)]
716#[error("JSON Pointer must start with `/`")]
717pub struct JsonPointerSyntaxError;
718
719/// An error returned when a [`JsonPointerTarget`] can't extract a typed value
720/// from a type-erased [`JsonPointee`] because the pointee's type doesn't match
721/// the target type.
722#[derive(Debug, thiserror::Error)]
723#[error("expected type `{expected}`; got `{actual}`")]
724pub struct JsonPointerTargetError {
725    /// The expected type name, from [`std::any::type_name`].
726    pub expected: &'static str,
727    /// The actual type name, from [`JsonPointee::name`].
728    pub actual: &'static str,
729}
730
731/// An error that occurs during traversal.
732#[derive(Debug, thiserror::Error)]
733pub enum JsonPointeeError {
734    #[error(transparent)]
735    Key(#[from] JsonPointerKeyError),
736    #[error("index {} out of range {}..{}", .0, .1.start, .1.end)]
737    Index(usize, Range<usize>),
738    #[error(transparent)]
739    Ty(#[from] JsonPointerTypeError),
740}
741
742/// An error that occurs when a pointed-to value doesn't have a key
743/// that the pointer references, with an optional suggestion
744/// for the correct key.
745#[derive(Debug)]
746pub struct JsonPointerKeyError {
747    pub key: String,
748    pub context: Option<JsonPointerKeyErrorContext>,
749}
750
751impl JsonPointerKeyError {
752    pub fn new(key: &JsonPointerSegment) -> Self {
753        Self {
754            key: key.to_str().into_owned(),
755            context: None,
756        }
757    }
758
759    #[cfg(feature = "did-you-mean")]
760    pub fn with_ty(key: &JsonPointerSegment, ty: JsonPointeeType) -> Self {
761        Self {
762            key: key.to_str().into_owned(),
763            context: Some(JsonPointerKeyErrorContext {
764                ty,
765                suggestion: None,
766            }),
767        }
768    }
769
770    #[cfg(feature = "did-you-mean")]
771    #[cold]
772    pub fn with_suggestions<'a>(
773        key: &JsonPointerSegment,
774        ty: JsonPointeeType,
775        suggestions: impl IntoIterator<Item = &'a str>,
776    ) -> Self {
777        let key = key.to_str();
778        let suggestion = suggestions
779            .into_iter()
780            .map(|suggestion| (suggestion, strsim::jaro_winkler(&key, suggestion)))
781            .max_by(|&(_, a), &(_, b)| {
782                // `strsim::jaro_winkler` returns the Jaro-Winkler _similarity_,
783                // not distance; so higher values mean the strings are closer.
784                a.partial_cmp(&b).unwrap_or(std::cmp::Ordering::Equal)
785            })
786            .map(|(suggestion, _)| suggestion.to_owned());
787        Self {
788            key: key.into_owned(),
789            context: Some(JsonPointerKeyErrorContext { ty, suggestion }),
790        }
791    }
792}
793
794impl std::error::Error for JsonPointerKeyError {}
795
796impl Display for JsonPointerKeyError {
797    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
798        match &self.context {
799            Some(JsonPointerKeyErrorContext {
800                ty,
801                suggestion: Some(suggestion),
802            }) => write!(
803                f,
804                "unknown key {:?} for value of {ty}; did you mean {suggestion:?}?",
805                self.key
806            ),
807            Some(JsonPointerKeyErrorContext {
808                ty,
809                suggestion: None,
810            }) => write!(f, "unknown key {:?} for value of {ty}", self.key),
811            None => write!(f, "unknown key {:?}", self.key),
812        }
813    }
814}
815
816#[derive(Debug)]
817pub struct JsonPointerKeyErrorContext {
818    pub ty: JsonPointeeType,
819    pub suggestion: Option<String>,
820}
821
822/// An error that occurs when a pointer can't be resolved
823/// against a value of the given type.
824#[derive(Debug)]
825pub struct JsonPointerTypeError {
826    pub pointer: String,
827    pub ty: Option<JsonPointeeType>,
828}
829
830impl JsonPointerTypeError {
831    pub fn new(pointer: &JsonPointer) -> Self {
832        Self {
833            pointer: pointer.to_string(),
834            ty: None,
835        }
836    }
837
838    #[cfg(feature = "did-you-mean")]
839    pub fn with_ty(pointer: &JsonPointer, ty: JsonPointeeType) -> Self {
840        Self {
841            pointer: pointer.to_string(),
842            ty: Some(ty),
843        }
844    }
845}
846
847impl std::error::Error for JsonPointerTypeError {}
848
849impl Display for JsonPointerTypeError {
850    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
851        match self.ty {
852            Some(ty) => write!(f, "can't resolve {:?} against value of {ty}", self.pointer),
853            None => write!(f, "can't resolve {:?}", self.pointer),
854        }
855    }
856}
857
858/// The name of a pointed-to type, for reporting traversal errors.
859#[derive(Clone, Copy, Debug, Eq, PartialEq)]
860pub enum JsonPointeeType {
861    Struct(JsonPointeeStructTy),
862    Variant(&'static str, JsonPointeeStructTy),
863    Named(&'static str),
864}
865
866impl JsonPointeeType {
867    #[inline]
868    pub fn struct_named(ty: &'static str) -> Self {
869        Self::Struct(JsonPointeeStructTy::Named(ty))
870    }
871
872    #[inline]
873    pub fn tuple_struct_named(ty: &'static str) -> Self {
874        Self::Struct(JsonPointeeStructTy::Tuple(ty))
875    }
876
877    #[inline]
878    pub fn unit_struct_named(ty: &'static str) -> Self {
879        Self::Struct(JsonPointeeStructTy::Unit(ty))
880    }
881
882    #[inline]
883    pub fn struct_variant_named(ty: &'static str, variant: &'static str) -> Self {
884        Self::Variant(ty, JsonPointeeStructTy::Named(variant))
885    }
886
887    #[inline]
888    pub fn tuple_variant_named(ty: &'static str, variant: &'static str) -> Self {
889        Self::Variant(ty, JsonPointeeStructTy::Tuple(variant))
890    }
891
892    #[inline]
893    pub fn unit_variant_named(ty: &'static str, variant: &'static str) -> Self {
894        Self::Variant(ty, JsonPointeeStructTy::Unit(variant))
895    }
896
897    #[inline]
898    pub fn named<T: ?Sized>() -> Self {
899        Self::Named(std::any::type_name::<T>())
900    }
901
902    #[inline]
903    pub fn name_of<T: ?Sized>(value: &T) -> Self {
904        Self::Named(std::any::type_name_of_val(value))
905    }
906}
907
908impl Display for JsonPointeeType {
909    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
910        match self {
911            Self::Struct(JsonPointeeStructTy::Named(ty)) => write!(f, "struct `{ty}`"),
912            Self::Struct(JsonPointeeStructTy::Tuple(ty)) => write!(f, "tuple struct `{ty}`"),
913            Self::Struct(JsonPointeeStructTy::Unit(ty)) => write!(f, "unit struct `{ty}`"),
914            Self::Variant(ty, JsonPointeeStructTy::Named(variant)) => {
915                write!(f, "variant `{variant}` of `{ty}`")
916            }
917            Self::Variant(ty, JsonPointeeStructTy::Tuple(variant)) => {
918                write!(f, "tuple variant `{variant}` of `{ty}`")
919            }
920            Self::Variant(ty, JsonPointeeStructTy::Unit(variant)) => {
921                write!(f, "unit variant `{variant}` of `{ty}`")
922            }
923            Self::Named(ty) => write!(f, "type `{ty}`"),
924        }
925    }
926}
927
928/// The name of a pointed-to struct type or enum variant,
929/// for reporting traversal errors.
930#[derive(Clone, Copy, Debug, Eq, PartialEq)]
931pub enum JsonPointeeStructTy {
932    Named(&'static str),
933    Tuple(&'static str),
934    Unit(&'static str),
935}
936
937#[cfg(test)]
938mod tests {
939    use super::*;
940
941    #[test]
942    fn test_segments() {
943        let pointer = JsonPointer::parse("/foo/bar/0").unwrap();
944        let mut segments = pointer.segments();
945        assert_eq!(segments.next().unwrap(), "foo");
946        assert_eq!(segments.next().unwrap(), "bar");
947        // `"0"` is parsed as a string segment, but implementations for `Vec`
948        // and tuple structs will parse it as an index.
949        assert_eq!(segments.next().unwrap(), "0");
950        assert_eq!(segments.next(), None);
951    }
952
953    #[test]
954    fn test_escaped_segments() {
955        let pointer = JsonPointer::parse("/foo~1bar/baz~0qux").unwrap();
956        let mut segments = pointer.segments();
957        // `~1` unescapes to `/`, `~0` unescapes to `~`.
958        assert_eq!(segments.next().unwrap(), "foo/bar");
959        assert_eq!(segments.next().unwrap(), "baz~qux");
960        assert_eq!(segments.next(), None);
961    }
962
963    #[test]
964    fn test_segment_display() {
965        let pointer = JsonPointer::parse("/foo~1bar").unwrap();
966        let segment = pointer.head().unwrap();
967        assert_eq!(segment.to_string(), "foo/bar");
968    }
969
970    #[test]
971    fn test_pointer_display() {
972        let input = "/foo/bar~1baz/0";
973        let pointer = JsonPointer::parse(input).unwrap();
974        assert_eq!(pointer.to_string(), input);
975    }
976
977    #[test]
978    fn test_pointer_buf() {
979        let pointer: Cow<'_, JsonPointer> = JsonPointer::parse("/foo/bar~0baz").unwrap().into();
980        let owned = pointer.into_owned();
981        let mut segments = owned.segments();
982        assert_eq!(segments.next().unwrap(), "foo");
983        assert_eq!(segments.next().unwrap(), "bar~baz");
984        assert_eq!(owned.to_string(), "/foo/bar~0baz");
985    }
986
987    #[test]
988    fn test_head_tail_single_segment() {
989        let pointer = JsonPointer::parse("/foo").unwrap();
990        assert_eq!(pointer.head().unwrap(), "foo");
991        assert!(pointer.tail().is_empty());
992    }
993
994    #[test]
995    fn test_tail_root_idempotent() {
996        let root = JsonPointer::empty();
997        assert!(root.tail().is_empty());
998        assert!(root.tail().tail().is_empty());
999    }
1000
1001    #[test]
1002    fn test_trailing_slash_produces_empty_segment() {
1003        let pointer = JsonPointer::parse("/foo/").unwrap();
1004        let mut segments = pointer.segments();
1005        assert_eq!(segments.next().unwrap(), "foo");
1006        assert_eq!(segments.next().unwrap(), "");
1007        assert_eq!(segments.next(), None);
1008
1009        // `head()` returns the first segment; `tail()` preserves the
1010        // trailing slash as a pointer with one empty segment.
1011        assert_eq!(pointer.head().unwrap(), "foo");
1012        let tail = pointer.tail();
1013        assert_eq!(tail.head().unwrap(), "");
1014        assert!(tail.tail().is_empty());
1015    }
1016
1017    #[test]
1018    fn test_consecutive_slashes() {
1019        let pointer = JsonPointer::parse("//").unwrap();
1020        let mut segments = pointer.segments();
1021        assert_eq!(segments.next().unwrap(), "");
1022        assert_eq!(segments.next().unwrap(), "");
1023        assert_eq!(segments.next(), None);
1024
1025        assert_eq!(pointer.head().unwrap(), "");
1026        let tail = pointer.tail();
1027        assert_eq!(tail.head().unwrap(), "");
1028        assert!(tail.tail().is_empty());
1029    }
1030
1031    #[test]
1032    fn test_parse_missing_leading_slash() {
1033        assert!(JsonPointer::parse("foo").is_err());
1034        assert!(JsonPointerBuf::parse("foo".to_owned()).is_err());
1035    }
1036
1037    #[test]
1038    fn test_resolve_vec() {
1039        let data = vec![1, 2, 3];
1040        let pointer = JsonPointer::parse("/1").unwrap();
1041        let result = data.resolve(pointer).unwrap() as &dyn Any;
1042        assert_eq!(result.downcast_ref::<i32>(), Some(&2));
1043    }
1044
1045    #[test]
1046    fn test_resolve_hashmap() {
1047        let mut data = HashMap::new();
1048        data.insert("foo".to_string(), 42);
1049
1050        let pointer = JsonPointer::parse("/foo").unwrap();
1051        let result = data.resolve(pointer).unwrap() as &dyn Any;
1052        assert_eq!(result.downcast_ref::<i32>(), Some(&42));
1053    }
1054
1055    #[test]
1056    fn test_resolve_option() {
1057        let data = Some(42);
1058        let pointer = JsonPointer::parse("").unwrap();
1059        let result = data.resolve(pointer).unwrap() as &dyn Any;
1060        assert_eq!(result.downcast_ref::<i32>(), Some(&42));
1061    }
1062
1063    #[test]
1064    fn test_primitive_empty_path() {
1065        let data = 42;
1066        let pointer = JsonPointer::parse("").unwrap();
1067        let result = data.resolve(pointer).unwrap() as &dyn Any;
1068        assert_eq!(result.downcast_ref::<i32>(), Some(&42));
1069    }
1070
1071    #[test]
1072    fn test_primitive_non_empty_path() {
1073        let data = 42;
1074        let pointer = JsonPointer::parse("/foo").unwrap();
1075        assert!(data.resolve(pointer).is_err());
1076    }
1077
1078    #[test]
1079    fn test_pointer_vec_element() {
1080        let data = vec![10, 20, 30];
1081        let result: i32 = data.pointer("/1").unwrap();
1082        assert_eq!(result, 20);
1083    }
1084
1085    #[test]
1086    fn test_pointer_hashmap_value() {
1087        let mut data = HashMap::new();
1088        data.insert("foo".to_owned(), 42);
1089        let result: i32 = data.pointer("/foo").unwrap();
1090        assert_eq!(result, 42);
1091    }
1092
1093    #[test]
1094    fn test_pointer_root() {
1095        let data = 42;
1096        let result: i32 = data.pointer("").unwrap();
1097        assert_eq!(result, 42);
1098    }
1099
1100    #[test]
1101    fn test_pointer_syntax_error() {
1102        let data = 42;
1103        assert!(matches!(
1104            data.pointer::<i32>("no-slash"),
1105            Err(JsonPointerError::Syntax(_))
1106        ));
1107    }
1108
1109    #[test]
1110    fn test_pointer_resolve_error() {
1111        let data = 42;
1112        assert!(matches!(
1113            data.pointer::<i32>("/foo"),
1114            Err(JsonPointerError::Resolve(_))
1115        ));
1116    }
1117
1118    #[test]
1119    fn test_pointer_cast_error() {
1120        let data = vec![42];
1121        let err = data.pointer::<&str>("/0").unwrap_err();
1122        assert!(matches!(err, JsonPointerError::Type { .. }));
1123    }
1124
1125    #[test]
1126    fn test_from_pointee_i32() {
1127        let data = 42i32;
1128        let pointee: &dyn JsonPointee = &data;
1129        let result = i32::from_pointee(pointee).unwrap();
1130        assert_eq!(result, 42);
1131    }
1132
1133    #[test]
1134    fn test_from_pointee_bool() {
1135        let data = true;
1136        let pointee: &dyn JsonPointee = &data;
1137        let result = bool::from_pointee(pointee).unwrap();
1138        assert!(result);
1139    }
1140
1141    #[test]
1142    fn test_from_pointee_wrong_type() {
1143        let data = 42i32;
1144        let pointee: &dyn JsonPointee = &data;
1145        let result = bool::from_pointee(pointee);
1146        assert!(result.is_err());
1147    }
1148}