1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::str::FromStr;
4
5use derive_more::Display;
6
7use crate::Error;
8
9fn decode_token(s: &str) -> String {
10 s.replace("~1", "/").replace("~0", "~")
11}
12
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19#[derive(Debug, Display, Clone, PartialEq, Eq, Hash)]
20#[display(fmt = "{}", .0)]
21pub struct Pointer<'a>(Cow<'a, str>);
22
23impl<'a> Pointer<'a> {
24 pub fn new(s: impl Into<Cow<'a, str>>) -> Result<Self, Error> {
41 let pointer = s.into();
42
43 if !pointer.is_empty() && !pointer.starts_with('/') {
44 Err(Error::MissingLeadingBackslash)
45 } else {
46 Ok(Self(pointer))
47 }
48 }
49
50 pub const fn root() -> Self {
52 Self(Cow::Borrowed(""))
53 }
54
55 pub fn is_root(&self) -> bool {
57 self.0.is_empty()
58 }
59
60 pub fn as_str(&self) -> &str {
62 &*self.0
63 }
64
65 pub fn key(&self) -> Option<String> {
80 self.0.rsplit_once('/').map(|(_, token)| decode_token(token))
81 }
82
83 pub fn parent(&self) -> Option<Pointer<'_>> {
98 self.0
99 .rsplit_once('/')
100 .map(|(parent, _)| Pointer(Cow::Borrowed(parent)))
101 }
102
103 pub fn ancestors(&self) -> impl Iterator<Item = Pointer<'_>> {
130 self.0
131 .match_indices('/')
132 .map(|(i, _)| i)
133 .chain([self.0.len()])
134 .rev()
135 .map(|i| Pointer(Cow::Borrowed(&self.0[0..i])))
136 }
137
138 pub fn is_ancestor_of(&self, other: &Pointer<'_>) -> bool {
142 other.ancestors().any(|pointer| pointer == *self)
143 }
144
145 pub fn is_parent_of(&self, other: &Pointer<'_>) -> bool {
149 other.parent().as_ref() == Some(self)
150 }
151
152 pub fn is_sibling_of(&self, other: &Pointer<'_>) -> bool {
154 self != other && self.parent() == other.parent()
155 }
156
157 pub fn depth(&self) -> usize {
159 self.0.split('/').skip(1).count()
160 }
161
162 pub fn into_owned(self) -> Pointer<'static> {
166 Pointer(Cow::Owned(self.0.into_owned()))
167 }
168
169 pub fn tokenize(&'a self) -> impl Iterator<Item = String> + 'a {
188 self.0.split('/').skip(1).map(decode_token)
189 }
190}
191
192impl FromStr for Pointer<'_> {
193 type Err = Error;
194
195 fn from_str(s: &str) -> Result<Self, Self::Err> {
196 Self::new(s.to_owned())
197 }
198}
199
200impl<'a> TryFrom<&'a str> for Pointer<'a> {
201 type Error = Error;
202
203 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
204 Self::new(s)
205 }
206}
207
208impl TryFrom<String> for Pointer<'_> {
209 type Error = Error;
210
211 fn try_from(s: String) -> Result<Self, Self::Error> {
212 Self::new(s)
213 }
214}
215
216impl AsRef<str> for Pointer<'_> {
217 fn as_ref(&self) -> &str {
218 self.as_str()
219 }
220}
221
222impl Ord for Pointer<'_> {
223 fn cmp(&self, other: &Self) -> Ordering {
224 match self.depth().cmp(&other.depth()) {
225 Ordering::Equal => self.0.cmp(&other.0),
226 ordering => ordering,
227 }
228 }
229}
230
231impl PartialOrd for Pointer<'_> {
232 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
233 Some(self.cmp(other))
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn it_accepts_valid_json_pointer() -> Result<(), Error> {
243 let tests = [
244 "",
246 "/",
248 "/path/to/object",
249 "/path/to/an/array/0/dummy",
250 ];
251
252 for s in tests {
253 let result = Pointer::new(s);
254
255 assert!(result.is_ok(), "'{}' is a valid JSON pointer", s);
256 }
257
258 Ok(())
259 }
260
261 #[test]
262 fn it_rejects_json_pointer_without_leading_backslash() {
263 let s = "path/without/leading/backslash";
264 let e = Pointer::new(s);
265
266 assert_eq!(e, Err(Error::MissingLeadingBackslash), "Invalid '{}' JSON pointer", s);
267 }
268
269 #[test]
270 fn it_detects_root_json_pointer() -> Result<(), Error> {
271 let tests = [Pointer::new("")?, Pointer::root()];
272
273 for pointer in tests {
274 assert!(pointer.is_root(), "'{}' is a root JSON pointer", pointer);
275 }
276
277 Ok(())
278 }
279
280 #[test]
281 fn it_rejects_non_root_json_pointer() -> Result<(), Error> {
282 let tests = [
283 Pointer::new("/")?,
284 Pointer::new("/dummy_path/to/something")?,
285 Pointer::new("/0/1/2/3")?,
286 ];
287
288 for pointer in tests {
289 assert!(!pointer.is_root(), "'{}' is not a root JSON pointer", pointer);
290 }
291
292 Ok(())
293 }
294
295 #[test]
296 fn it_gets_parent_json_pointer() -> Result<(), Error> {
297 let tests = [
298 (Pointer::root(), None),
299 (Pointer::new("/")?, Some(Pointer::root())),
300 (Pointer::new("/key")?, Some(Pointer::new("")?)),
301 (Pointer::new("/nested/key")?, Some(Pointer::new("/nested")?)),
302 (
303 Pointer::new("/deeper/nested/key")?,
304 Some(Pointer::new("/deeper/nested")?),
305 ),
306 ];
307
308 for (pointer, expected_parent_pointer) in tests {
309 assert_eq!(
310 pointer.parent(),
311 expected_parent_pointer,
312 "Parent of '{}' JSON pointer",
313 pointer,
314 );
315 }
316
317 Ok(())
318 }
319
320 #[test]
321 fn it_gets_key_from_json_pointer() -> Result<(), Error> {
322 let tests = [
323 (Pointer::root(), None),
324 (Pointer::new("/")?, Some("")),
325 (Pointer::new("/key")?, Some("key")),
326 (Pointer::new("/nested/key")?, Some("key")),
327 (Pointer::new("/deeper/nested/key")?, Some("key")),
328 (Pointer::new("/with_encoded_char/~1key")?, Some("/key")),
329 (Pointer::new("/with_encoded_char/~0key")?, Some("~key")),
330 (Pointer::new("/with_encoded_char/~10key")?, Some("/0key")),
331 (Pointer::new("/with_encoded_char/~01key")?, Some("~1key")),
332 ];
333
334 for (pointer, expected_key) in tests {
335 let expected_key = expected_key.map(ToString::to_string);
336 assert_eq!(pointer.key(), expected_key, "Key of '{}' JSON pointer", pointer);
337 }
338
339 Ok(())
340 }
341
342 #[test]
343 fn it_detects_parent_json_pointer() -> Result<(), Error> {
344 let tests = [
345 (Pointer::root(), Pointer::new("/")?),
346 (Pointer::new("/")?, Pointer::new("//a")?),
347 (Pointer::new("/foo/0")?, Pointer::new("/foo/0/zoo")?),
348 ];
349
350 for (pointer_a, pointer_b) in tests {
351 assert!(
352 pointer_a.is_parent_of(&pointer_b),
353 "'{}' is the parent of '{}' JSON pointer",
354 pointer_a,
355 pointer_b
356 );
357 }
358
359 Ok(())
360 }
361
362 #[test]
363 fn it_detects_non_parent_json_pointer() -> Result<(), Error> {
364 let tests = [
365 (Pointer::root(), Pointer::root()),
366 (Pointer::new("/a/b")?, Pointer::new("/a")?),
367 (Pointer::new("/a/b")?, Pointer::new("/a/b")?),
368 (Pointer::new("/a/b")?, Pointer::new("/a/b/c/d")?),
369 ];
370
371 for (pointer_a, pointer_b) in tests {
372 assert!(
373 !pointer_a.is_parent_of(&pointer_b),
374 "'{}' is not the parent of '{}' JSON pointer",
375 pointer_a,
376 pointer_b,
377 );
378 }
379
380 Ok(())
381 }
382
383 #[test]
384 fn it_detects_ancestor_json_pointer() -> Result<(), Error> {
385 let tests = [
386 (Pointer::root(), Pointer::root()),
387 (Pointer::root(), Pointer::new("/")?),
388 (Pointer::new("/")?, Pointer::new("//a")?),
389 (Pointer::new("/a/b")?, Pointer::new("/a/b")?),
390 (Pointer::new("/a/b/c")?, Pointer::new("/a/b/c/d/e/f/g")?),
391 (Pointer::new("/foo/0")?, Pointer::new("/foo/0/bar/zoo")?),
392 ];
393
394 for (pointer_a, pointer_b) in tests {
395 assert!(
396 pointer_a.is_ancestor_of(&pointer_b),
397 "'{}' is an ancestor of '{}' JSON pointer",
398 pointer_a,
399 pointer_b
400 );
401 }
402
403 Ok(())
404 }
405
406 #[test]
407 fn it_detects_non_ancestor_json_pointer() -> Result<(), Error> {
408 let tests = [
409 (Pointer::new("/a/b")?, Pointer::new("/a")?),
410 (Pointer::new("/0/foo/bar/zoo")?, Pointer::new("/1/foo/bar/zoo")?),
411 (Pointer::new("/tric")?, Pointer::new("/tricky/test")?),
412 ];
413
414 for (pointer_a, pointer_b) in tests {
415 assert!(
416 !pointer_a.is_ancestor_of(&pointer_b),
417 "'{}' is not an ancestor of '{}' JSON pointer",
418 pointer_a,
419 pointer_b,
420 );
421 }
422
423 Ok(())
424 }
425
426 #[test]
427 fn it_detects_sibling_json_pointer() -> Result<(), Error> {
428 let tests = [
429 (Pointer::new("/")?, Pointer::new("/a")?),
430 (Pointer::new("/a")?, Pointer::new("/")?),
431 (Pointer::new("/a/b/c")?, Pointer::new("/a/b/d")?),
432 (Pointer::new("/foo/bar/zoo/0")?, Pointer::new("/foo/bar/zoo/42")?),
433 ];
434
435 for (pointer_a, pointer_b) in tests {
436 assert!(
437 pointer_a.is_sibling_of(&pointer_b),
438 "'{}' is a sibling of '{}' JSON pointer",
439 pointer_a,
440 pointer_b
441 );
442 }
443
444 Ok(())
445 }
446
447 #[test]
448 fn it_detects_non_sibling_json_pointer() -> Result<(), Error> {
449 let tests = [
450 (Pointer::root(), Pointer::root()),
451 (Pointer::new("/b/d")?, Pointer::new("/b/d")?),
452 (Pointer::new("/b/d")?, Pointer::new("/a")?),
453 (Pointer::new("/a")?, Pointer::new("/b/d")?),
454 (Pointer::new("/a/b/c")?, Pointer::new("/d/e/f")?),
455 (Pointer::new("/0/foo/bar/zoo")?, Pointer::new("/1/foo/bar/zoo")?),
456 ];
457
458 for (pointer_a, pointer_b) in tests {
459 assert!(
460 !pointer_a.is_sibling_of(&pointer_b),
461 "'{}' is not a sibling of '{}' JSON pointer",
462 pointer_a,
463 pointer_b
464 );
465 }
466
467 Ok(())
468 }
469
470 #[test]
471 fn it_gets_ancestor_json_pointers() -> Result<(), Error> {
472 let tests = [
473 (Pointer::root(), vec![Pointer::root()]),
474 (Pointer::new("/")?, vec![Pointer::new("/")?, Pointer::root()]),
475 (
476 Pointer::new("/a/b")?,
477 vec![Pointer::new("/a/b")?, Pointer::new("/a")?, Pointer::root()],
478 ),
479 (
480 Pointer::new("/0/foo/bar/zoo")?,
481 vec![
482 Pointer::new("/0/foo/bar/zoo")?,
483 Pointer::new("/0/foo/bar")?,
484 Pointer::new("/0/foo")?,
485 Pointer::new("/0")?,
486 Pointer::root(),
487 ],
488 ),
489 ];
490
491 for (pointer, expected_ancestor_pointers) in tests {
492 let ancestor_pointers = pointer.ancestors().collect::<Vec<_>>();
493
494 assert_eq!(
495 ancestor_pointers, expected_ancestor_pointers,
496 "Ancestors of '{}' JSON pointer",
497 pointer
498 );
499 }
500
501 Ok(())
502 }
503
504 #[test]
505 fn it_gets_json_pointer_depth() -> Result<(), Error> {
506 let tests = [
507 (Pointer::root(), 0),
508 (Pointer::new("/")?, 1),
509 (Pointer::new("/a")?, 1),
510 (Pointer::new("/a/b/c")?, 3),
511 (Pointer::new("/foo/0/bar/1/zoo/2")?, 6),
512 ];
513
514 for (pointer, expected_depth) in tests {
515 assert_eq!(pointer.depth(), expected_depth, "Depth of '{}' JSON pointer", pointer);
516 }
517
518 Ok(())
519 }
520
521 #[test]
522 fn it_evaluates_json_pointer_into_tokens() -> Result<(), Error> {
523 let tests = [
524 (Pointer::root(), vec![]),
525 (Pointer::new("/")?, vec![""]),
526 (Pointer::new("/~1a")?, vec!["/a"]),
527 (Pointer::new("/~01a")?, vec!["~1a"]),
528 (Pointer::new("/~10a")?, vec!["/0a"]),
529 (Pointer::new("/~1a/~0b/c")?, vec!["/a", "~b", "c"]),
530 ];
531
532 for (pointer, expected_tokens) in tests {
533 let tokens = pointer.tokenize().collect::<Vec<_>>();
534 let tokens = tokens.iter().map(|s| s.as_str()).collect::<Vec<_>>();
535
536 assert_eq!(tokens, expected_tokens, "Tokens of '{}' JSON pointer", pointer);
537 }
538
539 Ok(())
540 }
541}