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
243impl PartialEq<str> for JsonPointerSegment {
244 #[inline]
245 fn eq(&self, other: &str) -> bool {
246 self.to_str() == other
247 }
248}
249
250impl PartialEq<JsonPointerSegment> for str {
251 #[inline]
252 fn eq(&self, other: &JsonPointerSegment) -> bool {
253 other == self
254 }
255}
256
257impl Display for JsonPointerSegment {
258 #[inline]
259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260 f.write_str(&self.to_str())
261 }
262}
263
264#[derive(Clone, Debug)]
266pub struct JsonPointerSegments<'a>(Option<Split<'a, char>>);
267
268impl<'a> Iterator for JsonPointerSegments<'a> {
269 type Item = &'a JsonPointerSegment;
270
271 #[inline]
272 fn next(&mut self) -> Option<Self::Item> {
273 self.0
274 .as_mut()
275 .and_then(|iter| iter.next())
276 .map(JsonPointerSegment::new)
277 }
278}
279
280impl<'a> DoubleEndedIterator for JsonPointerSegments<'a> {
281 #[inline]
282 fn next_back(&mut self) -> Option<Self::Item> {
283 self.0
284 .as_mut()
285 .and_then(|iter| iter.next_back())
286 .map(JsonPointerSegment::new)
287 }
288}
289
290impl FusedIterator for JsonPointerSegments<'_> {}
291
292macro_rules! impl_pointee_for {
293 () => {};
294 (#[$($attrs:tt)+] $ty:ty $(, $($rest:tt)*)?) => {
295 #[$($attrs)*]
296 impl_pointee_for!($ty);
297 $(impl_pointee_for!($($rest)*);)?
298 };
299 ($ty:ty $(, $($rest:tt)*)?) => {
300 impl JsonPointee for $ty {
301 fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
302 if pointer.is_empty() {
303 Ok(self)
304 } else {
305 Err({
306 #[cfg(feature = "did-you-mean")]
307 let err = JsonPointerTypeError::with_ty(
308 pointer,
309 JsonPointeeType::Named(stringify!($ty)),
310 );
311 #[cfg(not(feature = "did-you-mean"))]
312 let err = JsonPointerTypeError::new(pointer);
313 err
314 })?
315 }
316 }
317 }
318 $(impl_pointee_for!($($rest)*);)?
319 };
320}
321
322impl_pointee_for!(
323 i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize, f32, f64, bool, String, &'static str,
324 #[cfg(feature = "chrono")] chrono::DateTime<chrono::Utc>,
325 #[cfg(feature = "chrono")] chrono::NaiveDate,
326 #[cfg(feature = "url")] url::Url,
327);
328
329macro_rules! impl_copied_pointer_target_for {
330 () => {};
331 (#[$($attrs:tt)+] $ty:ty $(, $($rest:tt)*)?) => {
332 #[$($attrs)*]
333 impl_copied_pointer_target_for!($ty);
334 $(impl_copied_pointer_target_for!($($rest)*);)?
335 };
336 ($ty:ty $(, $($rest:tt)*)?) => {
337 impl<'a> JsonPointerTarget<'a> for $ty {
338 #[inline]
339 fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
340 let any: &dyn Any = pointee;
341 any.downcast_ref::<$ty>().copied().ok_or_else(|| JsonPointerTargetError {
342 expected: ::std::any::type_name::<$ty>(),
343 actual: pointee.name(),
344 })
345 }
346 }
347 $(impl_copied_pointer_target_for!($($rest)*);)?
348 };
349}
350
351impl_copied_pointer_target_for!(
352 i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize, f32, f64, bool,
353 #[cfg(feature = "chrono")] chrono::DateTime<chrono::Utc>,
354 #[cfg(feature = "chrono")] chrono::NaiveDate,
355);
356
357impl<'a> JsonPointerTarget<'a> for &'a str {
358 #[inline]
359 fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
360 let any: &dyn Any = pointee;
361 if let Some(s) = any.downcast_ref::<String>() {
362 Ok(s.as_str())
363 } else if let Some(&s) = any.downcast_ref::<&str>() {
364 Ok(s)
365 } else {
366 Err(JsonPointerTargetError {
367 expected: "str",
368 actual: pointee.name(),
369 })
370 }
371 }
372}
373
374impl<'a, T: JsonPointee> JsonPointerTarget<'a> for &'a [T] {
375 #[inline]
376 fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
377 let any: &dyn Any = pointee;
378 if let Some(v) = any.downcast_ref::<Vec<T>>() {
379 Ok(v.as_slice())
380 } else if let Some(v) = any.downcast_ref::<&[T]>() {
381 Ok(v)
382 } else {
383 Err(JsonPointerTargetError {
384 expected: ::std::any::type_name::<[T]>(),
385 actual: pointee.name(),
386 })
387 }
388 }
389}
390
391#[cfg(feature = "url")]
392impl<'a> JsonPointerTarget<'a> for &'a url::Url {
393 #[inline]
394 fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
395 let any: &dyn Any = pointee;
396 any.downcast_ref::<url::Url>()
397 .ok_or_else(|| JsonPointerTargetError {
398 expected: ::std::any::type_name::<url::Url>(),
399 actual: pointee.name(),
400 })
401 }
402}
403
404impl<'a, T: JsonPointee, H: BuildHasher + 'static> JsonPointerTarget<'a>
405 for &'a HashMap<String, T, H>
406{
407 #[inline]
408 fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
409 let any: &dyn Any = pointee;
410 any.downcast_ref::<HashMap<String, T, H>>()
411 .ok_or_else(|| JsonPointerTargetError {
412 expected: ::std::any::type_name::<HashMap<String, T, H>>(),
413 actual: pointee.name(),
414 })
415 }
416}
417
418impl<'a, T: JsonPointee> JsonPointerTarget<'a> for &'a BTreeMap<String, T> {
419 #[inline]
420 fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
421 let any: &dyn Any = pointee;
422 any.downcast_ref::<BTreeMap<String, T>>()
423 .ok_or_else(|| JsonPointerTargetError {
424 expected: ::std::any::type_name::<BTreeMap<String, T>>(),
425 actual: pointee.name(),
426 })
427 }
428}
429
430#[cfg(feature = "indexmap")]
431impl<'a, T: JsonPointee, H: BuildHasher + 'static> JsonPointerTarget<'a>
432 for &'a indexmap::IndexMap<String, T, H>
433{
434 #[inline]
435 fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
436 let any: &dyn Any = pointee;
437 any.downcast_ref::<indexmap::IndexMap<String, T, H>>()
438 .ok_or_else(|| JsonPointerTargetError {
439 expected: ::std::any::type_name::<indexmap::IndexMap<String, T, H>>(),
440 actual: pointee.name(),
441 })
442 }
443}
444
445#[cfg(feature = "serde_json")]
446impl<'a> JsonPointerTarget<'a> for &'a serde_json::Value {
447 #[inline]
448 fn from_pointee(pointee: &'a dyn JsonPointee) -> Result<Self, JsonPointerTargetError> {
449 let any: &dyn Any = pointee;
450 any.downcast_ref::<serde_json::Value>()
451 .ok_or_else(|| JsonPointerTargetError {
452 expected: ::std::any::type_name::<serde_json::Value>(),
453 actual: pointee.name(),
454 })
455 }
456}
457
458impl<T: JsonPointee> JsonPointee for Option<T> {
461 fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
462 match self {
463 Some(value) => value.resolve(pointer),
464 None => Err({
465 #[cfg(feature = "did-you-mean")]
466 let err = JsonPointerTypeError::with_ty(pointer, JsonPointeeType::name_of(self));
467 #[cfg(not(feature = "did-you-mean"))]
468 let err = JsonPointerTypeError::new(pointer);
469 err
470 })?,
471 }
472 }
473}
474
475impl<T: JsonPointee> JsonPointee for Box<T> {
476 fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
477 (**self).resolve(pointer)
478 }
479}
480
481impl<T: JsonPointee> JsonPointee for Arc<T> {
482 fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
483 (**self).resolve(pointer)
484 }
485}
486
487impl<T: JsonPointee> JsonPointee for Rc<T> {
488 fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
489 (**self).resolve(pointer)
490 }
491}
492
493impl<T: JsonPointee> JsonPointee for Vec<T> {
494 fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
495 let Some(key) = pointer.head() else {
496 return Ok(self);
497 };
498 if let Some(index) = key.to_index() {
499 if let Some(item) = self.get(index) {
500 item.resolve(pointer.tail())
501 } else {
502 Err(JsonPointeeError::Index(index, 0..self.len()))
503 }
504 } else {
505 Err({
506 #[cfg(feature = "did-you-mean")]
507 let err = JsonPointerTypeError::with_ty(pointer, JsonPointeeType::name_of(self));
508 #[cfg(not(feature = "did-you-mean"))]
509 let err = JsonPointerTypeError::new(pointer);
510 err
511 })?
512 }
513 }
514}
515
516impl<T, H> JsonPointee for HashMap<String, T, H>
517where
518 T: JsonPointee,
519 H: BuildHasher + 'static,
520{
521 fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
522 let Some(key) = pointer.head() else {
523 return Ok(self);
524 };
525 if let Some(value) = self.get(&*key.to_str()) {
526 value.resolve(pointer.tail())
527 } else {
528 Err({
529 #[cfg(feature = "did-you-mean")]
530 let err = JsonPointerKeyError::with_suggestions(
531 key,
532 JsonPointeeType::name_of(self),
533 self.keys().map(|key| key.as_str()),
534 );
535 #[cfg(not(feature = "did-you-mean"))]
536 let err = JsonPointerKeyError::new(key);
537 err
538 })?
539 }
540 }
541}
542
543impl<T: JsonPointee> JsonPointee for BTreeMap<String, T> {
544 fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
545 let Some(key) = pointer.head() else {
546 return Ok(self);
547 };
548 if let Some(value) = self.get(&*key.to_str()) {
549 value.resolve(pointer.tail())
550 } else {
551 Err({
552 #[cfg(feature = "did-you-mean")]
553 let err = JsonPointerKeyError::with_suggestions(
554 key,
555 JsonPointeeType::name_of(self),
556 self.keys().map(|key| key.as_str()),
557 );
558 #[cfg(not(feature = "did-you-mean"))]
559 let err = JsonPointerKeyError::new(key);
560 err
561 })?
562 }
563 }
564}
565
566#[cfg(feature = "indexmap")]
567impl<T, H> JsonPointee for indexmap::IndexMap<String, T, H>
568where
569 T: JsonPointee,
570 H: BuildHasher + 'static,
571{
572 fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
573 let Some(key) = pointer.head() else {
574 return Ok(self);
575 };
576 if let Some(value) = self.get(&*key.to_str()) {
577 value.resolve(pointer.tail())
578 } else {
579 Err({
580 #[cfg(feature = "did-you-mean")]
581 let err = JsonPointerKeyError::with_suggestions(
582 key,
583 JsonPointeeType::name_of(self),
584 self.keys().map(|key| key.as_str()),
585 );
586 #[cfg(not(feature = "did-you-mean"))]
587 let err = JsonPointerKeyError::new(key);
588 err
589 })?
590 }
591 }
592}
593
594#[cfg(feature = "serde_json")]
595impl JsonPointee for serde_json::Value {
596 fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
597 let Some(key) = pointer.head() else {
598 return Ok(self);
599 };
600 match self {
601 serde_json::Value::Object(map) => {
602 if let Some(value) = map.get(&*key.to_str()) {
603 value.resolve(pointer.tail())
604 } else {
605 Err({
606 #[cfg(feature = "did-you-mean")]
607 let err = JsonPointerKeyError::with_suggestions(
608 key,
609 JsonPointeeType::name_of(map),
610 map.keys().map(|key| key.as_str()),
611 );
612 #[cfg(not(feature = "did-you-mean"))]
613 let err = JsonPointerKeyError::new(key);
614 err
615 })?
616 }
617 }
618 serde_json::Value::Array(array) => {
619 let Some(index) = key.to_index() else {
620 return Err({
621 #[cfg(feature = "did-you-mean")]
622 let err =
623 JsonPointerTypeError::with_ty(pointer, JsonPointeeType::name_of(array));
624 #[cfg(not(feature = "did-you-mean"))]
625 let err = JsonPointerTypeError::new(pointer);
626 err
627 })?;
628 };
629 if let Some(item) = array.get(index) {
630 item.resolve(pointer.tail())
631 } else {
632 Err(JsonPointeeError::Index(index, 0..array.len()))
633 }
634 }
635 serde_json::Value::Null => Err({
636 #[cfg(feature = "did-you-mean")]
637 let err = JsonPointerKeyError::with_ty(key, JsonPointeeType::name_of(self));
638 #[cfg(not(feature = "did-you-mean"))]
639 let err = JsonPointerKeyError::new(key);
640 err
641 })?,
642 _ => Err({
643 #[cfg(feature = "did-you-mean")]
644 let err = JsonPointerTypeError::with_ty(pointer, JsonPointeeType::name_of(self));
645 #[cfg(not(feature = "did-you-mean"))]
646 let err = JsonPointerTypeError::new(pointer);
647 err
648 })?,
649 }
650 }
651}
652
653#[derive(Debug, thiserror::Error)]
655pub enum JsonPointerError {
656 #[error(transparent)]
657 Syntax(#[from] JsonPointerSyntaxError),
658 #[error(transparent)]
659 Resolve(#[from] JsonPointeeError),
660 #[error("at `{pointer}`: {source}")]
661 Type {
662 pointer: JsonPointerBuf,
663 #[source]
664 source: JsonPointerTargetError,
665 },
666}
667
668#[derive(Debug, thiserror::Error)]
670#[error("JSON Pointer must start with `/`")]
671pub struct JsonPointerSyntaxError;
672
673#[derive(Debug, thiserror::Error)]
677#[error("expected type `{expected}`; got `{actual}`")]
678pub struct JsonPointerTargetError {
679 pub expected: &'static str,
681 pub actual: &'static str,
683}
684
685#[derive(Debug, thiserror::Error)]
687pub enum JsonPointeeError {
688 #[error(transparent)]
689 Key(#[from] JsonPointerKeyError),
690 #[error("index {} out of range {}..{}", .0, .1.start, .1.end)]
691 Index(usize, Range<usize>),
692 #[error(transparent)]
693 Ty(#[from] JsonPointerTypeError),
694}
695
696#[derive(Debug)]
700pub struct JsonPointerKeyError {
701 pub key: String,
702 pub context: Option<JsonPointerKeyErrorContext>,
703}
704
705impl JsonPointerKeyError {
706 pub fn new(key: &JsonPointerSegment) -> Self {
707 Self {
708 key: key.to_str().into_owned(),
709 context: None,
710 }
711 }
712
713 #[cfg(feature = "did-you-mean")]
714 pub fn with_ty(key: &JsonPointerSegment, ty: JsonPointeeType) -> Self {
715 Self {
716 key: key.to_str().into_owned(),
717 context: Some(JsonPointerKeyErrorContext {
718 ty,
719 suggestion: None,
720 }),
721 }
722 }
723
724 #[cfg(feature = "did-you-mean")]
725 #[cold]
726 pub fn with_suggestions<'a>(
727 key: &JsonPointerSegment,
728 ty: JsonPointeeType,
729 suggestions: impl IntoIterator<Item = &'a str>,
730 ) -> Self {
731 let key = key.to_str();
732 let suggestion = suggestions
733 .into_iter()
734 .map(|suggestion| (suggestion, strsim::jaro_winkler(&key, suggestion)))
735 .max_by(|&(_, a), &(_, b)| {
736 a.partial_cmp(&b).unwrap_or(std::cmp::Ordering::Equal)
739 })
740 .map(|(suggestion, _)| suggestion.to_owned());
741 Self {
742 key: key.into_owned(),
743 context: Some(JsonPointerKeyErrorContext { ty, suggestion }),
744 }
745 }
746}
747
748impl std::error::Error for JsonPointerKeyError {}
749
750impl Display for JsonPointerKeyError {
751 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
752 match &self.context {
753 Some(JsonPointerKeyErrorContext {
754 ty,
755 suggestion: Some(suggestion),
756 }) => write!(
757 f,
758 "unknown key {:?} for value of {ty}; did you mean {suggestion:?}?",
759 self.key
760 ),
761 Some(JsonPointerKeyErrorContext {
762 ty,
763 suggestion: None,
764 }) => write!(f, "unknown key {:?} for value of {ty}", self.key),
765 None => write!(f, "unknown key {:?}", self.key),
766 }
767 }
768}
769
770#[derive(Debug)]
771pub struct JsonPointerKeyErrorContext {
772 pub ty: JsonPointeeType,
773 pub suggestion: Option<String>,
774}
775
776#[derive(Debug)]
779pub struct JsonPointerTypeError {
780 pub pointer: String,
781 pub ty: Option<JsonPointeeType>,
782}
783
784impl JsonPointerTypeError {
785 pub fn new(pointer: &JsonPointer) -> Self {
786 Self {
787 pointer: pointer.to_string(),
788 ty: None,
789 }
790 }
791
792 #[cfg(feature = "did-you-mean")]
793 pub fn with_ty(pointer: &JsonPointer, ty: JsonPointeeType) -> Self {
794 Self {
795 pointer: pointer.to_string(),
796 ty: Some(ty),
797 }
798 }
799}
800
801impl std::error::Error for JsonPointerTypeError {}
802
803impl Display for JsonPointerTypeError {
804 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
805 match self.ty {
806 Some(ty) => write!(f, "can't resolve {:?} against value of {ty}", self.pointer),
807 None => write!(f, "can't resolve {:?}", self.pointer),
808 }
809 }
810}
811
812#[derive(Clone, Copy, Debug, Eq, PartialEq)]
814pub enum JsonPointeeType {
815 Struct(JsonPointeeStructTy),
816 Variant(&'static str, JsonPointeeStructTy),
817 Named(&'static str),
818}
819
820impl JsonPointeeType {
821 #[inline]
822 pub fn struct_named(ty: &'static str) -> Self {
823 Self::Struct(JsonPointeeStructTy::Named(ty))
824 }
825
826 #[inline]
827 pub fn tuple_struct_named(ty: &'static str) -> Self {
828 Self::Struct(JsonPointeeStructTy::Tuple(ty))
829 }
830
831 #[inline]
832 pub fn unit_struct_named(ty: &'static str) -> Self {
833 Self::Struct(JsonPointeeStructTy::Unit(ty))
834 }
835
836 #[inline]
837 pub fn struct_variant_named(ty: &'static str, variant: &'static str) -> Self {
838 Self::Variant(ty, JsonPointeeStructTy::Named(variant))
839 }
840
841 #[inline]
842 pub fn tuple_variant_named(ty: &'static str, variant: &'static str) -> Self {
843 Self::Variant(ty, JsonPointeeStructTy::Tuple(variant))
844 }
845
846 #[inline]
847 pub fn unit_variant_named(ty: &'static str, variant: &'static str) -> Self {
848 Self::Variant(ty, JsonPointeeStructTy::Unit(variant))
849 }
850
851 #[inline]
852 pub fn named<T: ?Sized>() -> Self {
853 Self::Named(std::any::type_name::<T>())
854 }
855
856 #[inline]
857 pub fn name_of<T: ?Sized>(value: &T) -> Self {
858 Self::Named(std::any::type_name_of_val(value))
859 }
860}
861
862impl Display for JsonPointeeType {
863 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
864 match self {
865 Self::Struct(JsonPointeeStructTy::Named(ty)) => write!(f, "struct `{ty}`"),
866 Self::Struct(JsonPointeeStructTy::Tuple(ty)) => write!(f, "tuple struct `{ty}`"),
867 Self::Struct(JsonPointeeStructTy::Unit(ty)) => write!(f, "unit struct `{ty}`"),
868 Self::Variant(ty, JsonPointeeStructTy::Named(variant)) => {
869 write!(f, "variant `{variant}` of `{ty}`")
870 }
871 Self::Variant(ty, JsonPointeeStructTy::Tuple(variant)) => {
872 write!(f, "tuple variant `{variant}` of `{ty}`")
873 }
874 Self::Variant(ty, JsonPointeeStructTy::Unit(variant)) => {
875 write!(f, "unit variant `{variant}` of `{ty}`")
876 }
877 Self::Named(ty) => write!(f, "type `{ty}`"),
878 }
879 }
880}
881
882#[derive(Clone, Copy, Debug, Eq, PartialEq)]
885pub enum JsonPointeeStructTy {
886 Named(&'static str),
887 Tuple(&'static str),
888 Unit(&'static str),
889}
890
891#[cfg(test)]
892mod tests {
893 use super::*;
894
895 #[test]
896 fn test_segments() {
897 let pointer = JsonPointer::parse("/foo/bar/0").unwrap();
898 let mut segments = pointer.segments();
899 assert_eq!(segments.next().unwrap(), "foo");
900 assert_eq!(segments.next().unwrap(), "bar");
901 assert_eq!(segments.next().unwrap(), "0");
904 assert_eq!(segments.next(), None);
905 }
906
907 #[test]
908 fn test_escaped_segments() {
909 let pointer = JsonPointer::parse("/foo~1bar/baz~0qux").unwrap();
910 let mut segments = pointer.segments();
911 assert_eq!(segments.next().unwrap(), "foo/bar");
913 assert_eq!(segments.next().unwrap(), "baz~qux");
914 assert_eq!(segments.next(), None);
915 }
916
917 #[test]
918 fn test_segment_display() {
919 let pointer = JsonPointer::parse("/foo~1bar").unwrap();
920 let segment = pointer.head().unwrap();
921 assert_eq!(segment.to_string(), "foo/bar");
922 }
923
924 #[test]
925 fn test_pointer_display() {
926 let input = "/foo/bar~1baz/0";
927 let pointer = JsonPointer::parse(input).unwrap();
928 assert_eq!(pointer.to_string(), input);
929 }
930
931 #[test]
932 fn test_pointer_buf() {
933 let pointer: Cow<'_, JsonPointer> = JsonPointer::parse("/foo/bar~0baz").unwrap().into();
934 let owned = pointer.into_owned();
935 let mut segments = owned.segments();
936 assert_eq!(segments.next().unwrap(), "foo");
937 assert_eq!(segments.next().unwrap(), "bar~baz");
938 assert_eq!(owned.to_string(), "/foo/bar~0baz");
939 }
940
941 #[test]
942 fn test_head_tail_single_segment() {
943 let pointer = JsonPointer::parse("/foo").unwrap();
944 assert_eq!(pointer.head().unwrap(), "foo");
945 assert!(pointer.tail().is_empty());
946 }
947
948 #[test]
949 fn test_tail_root_idempotent() {
950 let root = JsonPointer::empty();
951 assert!(root.tail().is_empty());
952 assert!(root.tail().tail().is_empty());
953 }
954
955 #[test]
956 fn test_trailing_slash_produces_empty_segment() {
957 let pointer = JsonPointer::parse("/foo/").unwrap();
958 let mut segments = pointer.segments();
959 assert_eq!(segments.next().unwrap(), "foo");
960 assert_eq!(segments.next().unwrap(), "");
961 assert_eq!(segments.next(), None);
962
963 assert_eq!(pointer.head().unwrap(), "foo");
966 let tail = pointer.tail();
967 assert_eq!(tail.head().unwrap(), "");
968 assert!(tail.tail().is_empty());
969 }
970
971 #[test]
972 fn test_consecutive_slashes() {
973 let pointer = JsonPointer::parse("//").unwrap();
974 let mut segments = pointer.segments();
975 assert_eq!(segments.next().unwrap(), "");
976 assert_eq!(segments.next().unwrap(), "");
977 assert_eq!(segments.next(), None);
978
979 assert_eq!(pointer.head().unwrap(), "");
980 let tail = pointer.tail();
981 assert_eq!(tail.head().unwrap(), "");
982 assert!(tail.tail().is_empty());
983 }
984
985 #[test]
986 fn test_parse_missing_leading_slash() {
987 assert!(JsonPointer::parse("foo").is_err());
988 assert!(JsonPointerBuf::parse("foo".to_owned()).is_err());
989 }
990
991 #[test]
992 fn test_resolve_vec() {
993 let data = vec![1, 2, 3];
994 let pointer = JsonPointer::parse("/1").unwrap();
995 let result = data.resolve(pointer).unwrap() as &dyn Any;
996 assert_eq!(result.downcast_ref::<i32>(), Some(&2));
997 }
998
999 #[test]
1000 fn test_resolve_hashmap() {
1001 let mut data = HashMap::new();
1002 data.insert("foo".to_string(), 42);
1003
1004 let pointer = JsonPointer::parse("/foo").unwrap();
1005 let result = data.resolve(pointer).unwrap() as &dyn Any;
1006 assert_eq!(result.downcast_ref::<i32>(), Some(&42));
1007 }
1008
1009 #[test]
1010 fn test_resolve_option() {
1011 let data = Some(42);
1012 let pointer = JsonPointer::parse("").unwrap();
1013 let result = data.resolve(pointer).unwrap() as &dyn Any;
1014 assert_eq!(result.downcast_ref::<i32>(), Some(&42));
1015 }
1016
1017 #[test]
1018 fn test_primitive_empty_path() {
1019 let data = 42;
1020 let pointer = JsonPointer::parse("").unwrap();
1021 let result = data.resolve(pointer).unwrap() as &dyn Any;
1022 assert_eq!(result.downcast_ref::<i32>(), Some(&42));
1023 }
1024
1025 #[test]
1026 fn test_primitive_non_empty_path() {
1027 let data = 42;
1028 let pointer = JsonPointer::parse("/foo").unwrap();
1029 assert!(data.resolve(pointer).is_err());
1030 }
1031
1032 #[test]
1033 fn test_pointer_vec_element() {
1034 let data = vec![10, 20, 30];
1035 let result: i32 = data.pointer("/1").unwrap();
1036 assert_eq!(result, 20);
1037 }
1038
1039 #[test]
1040 fn test_pointer_hashmap_value() {
1041 let mut data = HashMap::new();
1042 data.insert("foo".to_owned(), 42);
1043 let result: i32 = data.pointer("/foo").unwrap();
1044 assert_eq!(result, 42);
1045 }
1046
1047 #[test]
1048 fn test_pointer_root() {
1049 let data = 42;
1050 let result: i32 = data.pointer("").unwrap();
1051 assert_eq!(result, 42);
1052 }
1053
1054 #[test]
1055 fn test_pointer_syntax_error() {
1056 let data = 42;
1057 assert!(matches!(
1058 data.pointer::<i32>("no-slash"),
1059 Err(JsonPointerError::Syntax(_))
1060 ));
1061 }
1062
1063 #[test]
1064 fn test_pointer_resolve_error() {
1065 let data = 42;
1066 assert!(matches!(
1067 data.pointer::<i32>("/foo"),
1068 Err(JsonPointerError::Resolve(_))
1069 ));
1070 }
1071
1072 #[test]
1073 fn test_pointer_cast_error() {
1074 let data = vec![42];
1075 let err = data.pointer::<&str>("/0").unwrap_err();
1076 assert!(matches!(err, JsonPointerError::Type { .. }));
1077 }
1078
1079 #[test]
1080 fn test_from_pointee_i32() {
1081 let data = 42i32;
1082 let pointee: &dyn JsonPointee = &data;
1083 let result = i32::from_pointee(pointee).unwrap();
1084 assert_eq!(result, 42);
1085 }
1086
1087 #[test]
1088 fn test_from_pointee_bool() {
1089 let data = true;
1090 let pointee: &dyn JsonPointee = &data;
1091 let result = bool::from_pointee(pointee).unwrap();
1092 assert!(result);
1093 }
1094
1095 #[test]
1096 fn test_from_pointee_wrong_type() {
1097 let data = 42i32;
1098 let pointee: &dyn JsonPointee = &data;
1099 let result = bool::from_pointee(pointee);
1100 assert!(result.is_err());
1101 }
1102}