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