1use std::fmt;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum JsonPointerError {
13 InvalidEscape,
15 InvalidUtf8,
17}
18
19impl fmt::Display for JsonPointerError {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 JsonPointerError::InvalidEscape => {
23 write!(
24 f,
25 "invalid JSON Pointer escape: ~ must be followed by 0 or 1"
26 )
27 }
28 JsonPointerError::InvalidUtf8 => write!(f, "JSON Pointer is not valid UTF-8"),
29 }
30 }
31}
32
33impl std::error::Error for JsonPointerError {}
34
35fn encode_segment(segment: &str) -> String {
37 segment.replace('~', "~0").replace('/', "~1")
38}
39
40fn decode_token(token: &str) -> Result<String, JsonPointerError> {
42 let mut out = String::with_capacity(token.len());
43 let mut chars = token.chars().peekable();
44 while let Some(c) = chars.next() {
45 if c == '~' {
46 let next = chars.next().ok_or(JsonPointerError::InvalidEscape)?;
47 match next {
48 '0' => out.push('~'),
49 '1' => out.push('/'),
50 _ => return Err(JsonPointerError::InvalidEscape),
51 }
52 } else {
53 out.push(c);
54 }
55 }
56 Ok(out)
57}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct JsonPointer {
65 segments: Vec<String>,
67 encoded: String,
69}
70
71impl JsonPointer {
72 #[must_use]
74 pub fn root() -> Self {
75 Self {
76 segments: Vec::new(),
77 encoded: String::new(),
78 }
79 }
80
81 fn from_segments_and_encoded(segments: Vec<String>, encoded: String) -> Self {
82 Self { segments, encoded }
83 }
84
85 #[must_use]
88 pub fn push(&self, segment: &str) -> Self {
89 let mut new_segments = self.segments.clone();
90 new_segments.push(segment.to_string());
91 let encoded = if new_segments.is_empty() {
92 String::new()
93 } else {
94 format!(
95 "/{}",
96 new_segments
97 .iter()
98 .map(String::as_str)
99 .map(encode_segment)
100 .collect::<Vec<_>>()
101 .join("/")
102 )
103 };
104 Self {
105 segments: new_segments,
106 encoded,
107 }
108 }
109
110 #[must_use]
112 pub fn pop(&self) -> Self {
113 if self.segments.is_empty() {
114 return self.clone();
115 }
116 let mut segs = self.segments.clone();
117 segs.pop();
118 let enc = if segs.is_empty() {
119 String::new()
120 } else {
121 format!(
122 "/{}",
123 segs.iter()
124 .map(String::as_str)
125 .map(encode_segment)
126 .collect::<Vec<_>>()
127 .join("/")
128 )
129 };
130 Self {
131 segments: segs,
132 encoded: enc,
133 }
134 }
135
136 #[must_use]
138 pub fn parent(&self) -> Self {
139 self.pop()
140 }
141
142 #[must_use]
144 pub fn truncate(&self, len: usize) -> Self {
145 if len >= self.segments.len() {
146 return self.clone();
147 }
148 let segs: Vec<String> = self.segments[..len].to_vec();
149 let enc = if segs.is_empty() {
150 String::new()
151 } else {
152 format!(
153 "/{}",
154 segs.iter()
155 .map(String::as_str)
156 .map(encode_segment)
157 .collect::<Vec<_>>()
158 .join("/")
159 )
160 };
161 Self {
162 segments: segs,
163 encoded: enc,
164 }
165 }
166
167 #[must_use]
169 pub fn len(&self) -> usize {
170 self.segments.len()
171 }
172
173 #[must_use]
175 pub fn is_empty(&self) -> bool {
176 self.segments.is_empty()
177 }
178
179 #[must_use]
181 pub fn is_root(&self) -> bool {
182 self.segments.is_empty()
183 }
184
185 pub fn segments(&self) -> impl Iterator<Item = &str> {
187 self.segments.iter().map(String::as_str)
188 }
189
190 #[must_use]
192 pub fn segment_at(&self, index: usize) -> Option<&str> {
193 self.segments.get(index).map(String::as_str)
194 }
195
196 #[must_use]
198 pub fn remove(&self, index: usize) -> Self {
199 if index >= self.segments.len() {
200 return self.clone();
201 }
202 let mut segs = self.segments.clone();
203 segs.remove(index);
204 let enc = if segs.is_empty() {
205 String::new()
206 } else {
207 format!(
208 "/{}",
209 segs.iter()
210 .map(String::as_str)
211 .map(encode_segment)
212 .collect::<Vec<_>>()
213 .join("/")
214 )
215 };
216 Self {
217 segments: segs,
218 encoded: enc,
219 }
220 }
221
222 #[must_use]
224 pub fn as_str(&self) -> &str {
225 self.encoded.as_str()
226 }
227
228 #[must_use]
230 pub fn display_root_or_path(&self) -> &str {
231 if self.encoded.is_empty() {
232 "root"
233 } else {
234 self.encoded.as_str()
235 }
236 }
237}
238
239impl fmt::Display for JsonPointer {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 write!(f, "{}", self.encoded)
242 }
243}
244
245fn try_parse(s: &str) -> Result<JsonPointer, JsonPointerError> {
247 if s.is_empty() {
248 return Ok(JsonPointer::root());
249 }
250 if !s.starts_with('/') {
251 return Err(JsonPointerError::InvalidEscape);
252 }
253 let parts: Vec<&str> = s.split('/').collect();
254 let mut segments = Vec::with_capacity(parts.len().saturating_sub(1));
255 for (i, part) in parts.iter().enumerate() {
256 if i == 0 {
257 continue;
258 }
259 segments.push(decode_token(part)?);
260 }
261 let encoded = s.to_string();
262 Ok(JsonPointer::from_segments_and_encoded(segments, encoded))
263}
264
265impl TryFrom<&str> for JsonPointer {
266 type Error = JsonPointerError;
267
268 fn try_from(s: &str) -> Result<Self, Self::Error> {
269 try_parse(s)
270 }
271}
272
273impl TryFrom<String> for JsonPointer {
274 type Error = JsonPointerError;
275
276 fn try_from(s: String) -> Result<Self, Self::Error> {
277 try_parse(&s)
278 }
279}
280
281impl TryFrom<&[u8]> for JsonPointer {
282 type Error = JsonPointerError;
283
284 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
285 let s = std::str::from_utf8(bytes).map_err(|_| JsonPointerError::InvalidUtf8)?;
286 try_parse(s)
287 }
288}
289
290impl TryFrom<Vec<u8>> for JsonPointer {
291 type Error = JsonPointerError;
292
293 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
294 let s = String::from_utf8(bytes).map_err(|_| JsonPointerError::InvalidUtf8)?;
295 try_parse(&s)
296 }
297}
298
299impl From<Vec<String>> for JsonPointer {
300 fn from(segments: Vec<String>) -> Self {
301 let encoded = if segments.is_empty() {
302 String::new()
303 } else {
304 format!(
305 "/{}",
306 segments
307 .iter()
308 .map(String::as_str)
309 .map(encode_segment)
310 .collect::<Vec<_>>()
311 .join("/")
312 )
313 };
314 Self { segments, encoded }
315 }
316}
317
318impl From<JsonPointer> for String {
319 fn from(p: JsonPointer) -> Self {
320 p.encoded
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use super::{JsonPointer, JsonPointerError};
327 use std::convert::TryFrom;
328
329 #[test]
330 fn root_is_empty() {
331 let p: JsonPointer = JsonPointer::root();
332 let expected = "";
333 let actual = p.as_str();
334 assert_eq!(expected, actual);
335 assert_eq!(p.to_string(), "");
336 assert!(p.is_root());
337 assert_eq!(p.len(), 0);
338 }
339
340 #[test]
341 fn push_one_segment() {
342 let p: JsonPointer = JsonPointer::root().push("foo");
343 let expected = "/foo";
344 let actual = p.as_str();
345 assert_eq!(expected, actual);
346 }
347
348 #[test]
349 fn push_multiple_segments() {
350 let p: JsonPointer = JsonPointer::root().push("a").push("b").push("c");
351 let expected = "/a/b/c";
352 let actual = p.as_str();
353 assert_eq!(expected, actual);
354 }
355
356 #[test]
357 fn segment_with_slash_encoded_as_tilde1() {
358 let p: JsonPointer = JsonPointer::root().push("a/b");
359 let expected = "/a~1b";
360 let actual = p.as_str();
361 assert_eq!(expected, actual);
362 }
363
364 #[test]
365 fn segment_with_tilde_encoded_as_tilde0() {
366 let p: JsonPointer = JsonPointer::root().push("a~b");
367 let expected = "/a~0b";
368 let actual = p.as_str();
369 assert_eq!(expected, actual);
370 }
371
372 #[test]
373 fn segment_with_both_tilde_and_slash() {
374 let p: JsonPointer = JsonPointer::root().push("~1").push("a/b");
375 let expected = "/~01/a~1b";
376 let actual = p.as_str();
377 assert_eq!(expected, actual);
378 }
379
380 #[test]
381 fn display_root_or_path() {
382 let root: JsonPointer = JsonPointer::root();
383 let expected = "root";
384 let actual = root.display_root_or_path();
385 assert_eq!(expected, actual);
386 let child: JsonPointer = JsonPointer::root().push("x");
387 let expected_path = "/x";
388 let actual_path = child.display_root_or_path();
389 assert_eq!(expected_path, actual_path);
390 }
391
392 #[test]
393 fn pop_from_one_segment_gives_root() {
394 let p: JsonPointer = JsonPointer::root().push("a");
395 let expected = JsonPointer::root();
396 let actual = p.pop();
397 assert_eq!(expected.as_str(), actual.as_str());
398 assert_eq!(actual.len(), 0);
399 }
400
401 #[test]
402 fn pop_from_three_segments_gives_two() {
403 let p: JsonPointer = JsonPointer::root().push("a").push("b").push("c");
404 let expected = JsonPointer::root().push("a").push("b");
405 let actual = p.pop();
406 assert_eq!(expected.as_str(), actual.as_str());
407 }
408
409 #[test]
410 fn pop_from_root_leaves_root() {
411 let root: JsonPointer = JsonPointer::root();
412 let actual = root.pop();
413 assert_eq!(root.as_str(), actual.as_str());
414 }
415
416 #[test]
417 fn parent_matches_pop() {
418 let p: JsonPointer = JsonPointer::root().push("x").push("y");
419 let expected = p.pop();
420 let actual = p.parent();
421 assert_eq!(expected.as_str(), actual.as_str());
422 }
423
424 #[test]
425 fn truncate_to_zero_gives_root() {
426 let p: JsonPointer = JsonPointer::root().push("a").push("b");
427 let actual = p.truncate(0);
428 assert_eq!(JsonPointer::root().as_str(), actual.as_str());
429 }
430
431 #[test]
432 fn truncate_to_one() {
433 let p: JsonPointer = JsonPointer::root().push("a").push("b").push("c");
434 let expected = JsonPointer::root().push("a");
435 let actual = p.truncate(1);
436 assert_eq!(expected.as_str(), actual.as_str());
437 }
438
439 #[test]
440 fn truncate_to_current_len_unchanged() {
441 let p: JsonPointer = JsonPointer::root().push("a").push("b");
442 let actual = p.truncate(2);
443 assert_eq!(p.as_str(), actual.as_str());
444 }
445
446 #[test]
447 fn truncate_beyond_len_unchanged() {
448 let p: JsonPointer = JsonPointer::root().push("a");
449 let actual = p.truncate(10);
450 assert_eq!(p.as_str(), actual.as_str());
451 }
452
453 #[test]
454 fn segments_iterator() {
455 let p: JsonPointer = JsonPointer::root().push("a").push("b").push("c");
456 let expected: Vec<&str> = vec!["a", "b", "c"];
457 let actual: Vec<&str> = p.segments().collect();
458 assert_eq!(expected, actual);
459 }
460
461 #[test]
462 fn segment_at_valid() {
463 let p: JsonPointer = JsonPointer::root().push("x").push("y");
464 let expected_first = Some("x");
465 let actual_first = p.segment_at(0);
466 assert_eq!(expected_first, actual_first);
467 let expected_second = Some("y");
468 let actual_second = p.segment_at(1);
469 assert_eq!(expected_second, actual_second);
470 }
471
472 #[test]
473 fn segment_at_out_of_bounds() {
474 let p: JsonPointer = JsonPointer::root().push("a");
475 let expected: Option<&str> = None;
476 let actual = p.segment_at(1);
477 assert_eq!(expected, actual);
478 }
479
480 #[test]
481 fn remove_segment() {
482 let p: JsonPointer = JsonPointer::root().push("a").push("b").push("c");
483 let actual = p.remove(1);
484 let expected = JsonPointer::root().push("a").push("c");
485 assert_eq!(expected.as_str(), actual.as_str());
486 }
487
488 #[test]
489 fn try_from_empty_string() {
490 let expected = JsonPointer::root();
491 let actual = JsonPointer::try_from("").unwrap();
492 assert_eq!(expected.as_str(), actual.as_str());
493 }
494
495 #[test]
496 fn try_from_slash_a() {
497 let expected = JsonPointer::root().push("a");
498 let actual = JsonPointer::try_from("/a").unwrap();
499 assert_eq!(expected.as_str(), actual.as_str());
500 }
501
502 #[test]
503 fn try_from_slash_a_slash_b() {
504 let expected = JsonPointer::root().push("a").push("b");
505 let actual = JsonPointer::try_from("/a/b").unwrap();
506 assert_eq!(expected.as_str(), actual.as_str());
507 }
508
509 #[test]
510 fn try_from_encoded_slash() {
511 let expected = JsonPointer::root().push("a/b");
512 let actual = JsonPointer::try_from("/a~1b").unwrap();
513 assert_eq!(expected.as_str(), actual.as_str());
514 }
515
516 #[test]
517 fn try_from_encoded_tilde() {
518 let expected = JsonPointer::root().push("a~b");
519 let actual = JsonPointer::try_from("/a~0b").unwrap();
520 assert_eq!(expected.as_str(), actual.as_str());
521 }
522
523 #[test]
524 fn try_from_encoded_tilde1_segment() {
525 let expected = JsonPointer::root().push("~1");
526 let actual = JsonPointer::try_from("/~01").unwrap();
527 assert_eq!(expected.as_str(), actual.as_str());
528 }
529
530 #[test]
531 fn try_from_invalid_escape_tilde_only() {
532 let actual = JsonPointer::try_from("~");
533 assert!(matches!(actual, Err(JsonPointerError::InvalidEscape)));
534 }
535
536 #[test]
537 fn try_from_invalid_escape_tilde_two() {
538 let actual = JsonPointer::try_from("/a~2b");
539 assert!(matches!(actual, Err(JsonPointerError::InvalidEscape)));
540 }
541
542 #[test]
543 fn try_from_no_leading_slash_rejected() {
544 let actual = JsonPointer::try_from("a");
545 assert!(matches!(actual, Err(JsonPointerError::InvalidEscape)));
546 }
547
548 #[test]
549 fn from_vec_string_empty() {
550 let expected = JsonPointer::root();
551 let actual: JsonPointer = Vec::<String>::new().into();
552 assert_eq!(expected.as_str(), actual.as_str());
553 }
554
555 #[test]
556 fn from_vec_string_two() {
557 let expected = JsonPointer::root().push("a").push("b");
558 let actual: JsonPointer = vec!["a".to_string(), "b".to_string()].into();
559 assert_eq!(expected.as_str(), actual.as_str());
560 }
561
562 #[test]
563 fn try_from_bytes_valid_utf8() {
564 let expected = JsonPointer::root().push("x");
565 let actual = JsonPointer::try_from("/x".as_bytes()).unwrap();
566 assert_eq!(expected.as_str(), actual.as_str());
567 }
568
569 #[test]
570 fn try_from_bytes_invalid_utf8() {
571 let actual = JsonPointer::try_from(vec![0xff, 0xfe].as_slice());
572 assert!(matches!(actual, Err(JsonPointerError::InvalidUtf8)));
573 }
574
575 #[test]
576 fn into_string() {
577 let p: JsonPointer = JsonPointer::root().push("foo").push("bar");
578 let expected = "/foo/bar";
579 let actual: String = p.into();
580 assert_eq!(expected, actual);
581 }
582
583 #[test]
584 fn round_trip_build_serialize_parse() {
585 let expected = JsonPointer::root().push("a").push("b").push("c");
586 let s = expected.to_string();
587 let actual = JsonPointer::try_from(s.as_str()).unwrap();
588 assert_eq!(expected.as_str(), actual.as_str());
589 }
590
591 #[test]
592 fn round_trip_parse_serialize_parse() {
593 let s = "/a~1b/c~0d";
594 let p = JsonPointer::try_from(s).unwrap();
595 let s2 = p.to_string();
596 let p2 = JsonPointer::try_from(s2.as_str()).unwrap();
597 assert_eq!(p.as_str(), p2.as_str());
598 }
599
600 #[test]
601 fn empty_segment() {
602 let p: JsonPointer = JsonPointer::root().push("");
603 let expected = "/";
604 let actual = p.as_str();
605 assert_eq!(expected, actual);
606 }
607
608 #[test]
609 fn len_after_push() {
610 let root = JsonPointer::root();
611 assert_eq!(root.len(), 0);
612 let one = root.push("a");
613 assert_eq!(one.len(), 1);
614 let three = one.push("b").push("c");
615 assert_eq!(three.len(), 3);
616 }
617}