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#[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 #[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 #[inline]
43 pub fn empty() -> &'static Self {
44 JsonPointer::new("")
45 }
46
47 #[inline]
49 pub fn is_empty(&self) -> bool {
50 self.0.is_empty()
51 }
52
53 #[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 #[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 #[inline]
75 pub fn segments(&self) -> JsonPointerSegments<'_> {
76 JsonPointerSegments(self.0.strip_prefix('/').map(|raw| raw.split('/')))
77 }
78
79 #[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#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
117pub struct JsonPointerBuf(String);
118
119impl JsonPointerBuf {
120 #[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
178pub trait JsonPointee: Any {
180 fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError>;
182
183 #[inline]
185 fn name(&self) -> &'static str {
186 std::any::type_name::<Self>()
187 }
188}
189
190pub trait JsonPointerTarget<'a>: Sized {
192 fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError>;
194}
195
196pub trait JsonPointeeExt: JsonPointee {
198 #[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#[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 #[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 #[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 self.0.parse().ok()
237 }
238 _ => None,
239 }
240 }
241
242 #[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#[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
491impl<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#[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#[derive(Debug, thiserror::Error)]
716#[error("JSON Pointer must start with `/`")]
717pub struct JsonPointerSyntaxError;
718
719#[derive(Debug, thiserror::Error)]
723#[error("expected type `{expected}`; got `{actual}`")]
724pub struct JsonPointerTargetError {
725 pub expected: &'static str,
727 pub actual: &'static str,
729}
730
731#[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#[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 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#[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#[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#[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 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 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 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}