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