1use std::borrow::Cow;
2use std::fmt::{self, Display};
3
4use crate::coding::{Decode, DecodeError, Encode};
5
6pub trait IntoPathRef<'a>: Into<PathRef<'a>> {}
9
10impl<'a, T: Into<PathRef<'a>>> IntoPathRef<'a> for T {}
11
12#[derive(Debug, PartialEq, Eq, Hash, Clone)]
19pub struct PathRef<'a>(Cow<'a, str>);
20
21impl<'a> PathRef<'a> {
22 pub fn new(s: &'a str) -> Self {
27 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
28
29 if trimmed.contains("//") {
31 let normalized = trimmed
33 .split('/')
34 .filter(|s| !s.is_empty())
35 .collect::<Vec<_>>()
36 .join("/");
37 Self(Cow::Owned(normalized))
38 } else {
39 Self(Cow::Borrowed(trimmed))
41 }
42 }
43
44 pub fn as_str(&self) -> &str {
46 &self.0
47 }
48
49 pub fn is_empty(&self) -> bool {
51 self.0.is_empty()
52 }
53
54 pub fn len(&self) -> usize {
56 self.0.len()
57 }
58
59 pub fn to_owned(&self) -> Path {
61 Path(self.0.clone().into_owned())
62 }
63}
64
65impl<'a> From<&'a str> for PathRef<'a> {
66 fn from(s: &'a str) -> Self {
67 Self::new(s)
68 }
69}
70
71impl<'a> From<&'a String> for PathRef<'a> {
72 fn from(s: &'a String) -> Self {
73 Self::new(s.as_str())
74 }
75}
76
77impl From<String> for PathRef<'static> {
78 fn from(s: String) -> Self {
79 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
81
82 if trimmed.contains("//") {
84 let normalized = trimmed
86 .split('/')
87 .filter(|s| !s.is_empty())
88 .collect::<Vec<_>>()
89 .join("/");
90 Self(Cow::Owned(normalized))
91 } else if trimmed == s {
92 Self(Cow::Owned(s))
94 } else {
95 Self(Cow::Owned(trimmed.to_string()))
97 }
98 }
99}
100
101impl<'a> From<&'a Path> for PathRef<'a> {
102 fn from(p: &'a Path) -> Self {
103 Self(Cow::Borrowed(p.0.as_str()))
105 }
106}
107
108impl<'a, 'b> From<&'a PathRef<'b>> for PathRef<'a>
109where
110 'b: 'a,
111{
112 fn from(p: &'a PathRef<'b>) -> Self {
113 Self(p.0.clone())
114 }
115}
116
117impl<'a> AsRef<str> for PathRef<'a> {
118 fn as_ref(&self) -> &str {
119 &self.0
120 }
121}
122
123impl<'a> Display for PathRef<'a> {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 write!(f, "{}", self.0)
126 }
127}
128
129#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
156#[cfg_attr(feature = "serde", derive(serde::Serialize))]
157pub struct Path(String);
158
159impl Path {
160 pub fn new<'a>(path: impl Into<PathRef<'a>>) -> Self {
166 Self(path.into().0.to_string())
168 }
169
170 pub fn has_prefix<'a>(&self, prefix: impl Into<PathRef<'a>>) -> bool {
192 let prefix = prefix.into();
193 if prefix.is_empty() {
194 return true;
195 }
196
197 if !self.0.starts_with(prefix.as_str()) {
198 return false;
199 }
200
201 if self.0.len() == prefix.len() {
203 return true;
204 }
205
206 self.0.chars().nth(prefix.len()) == Some('/')
208 }
209
210 pub fn strip_prefix<'a>(&'a self, prefix: impl Into<PathRef<'a>>) -> Option<PathRef<'a>> {
226 let prefix = prefix.into();
227 if !self.has_prefix(&prefix) {
228 return None;
229 }
230
231 let suffix = &self.0[prefix.len()..];
232 let suffix = suffix.trim_start_matches('/');
234 Some(PathRef(Cow::Borrowed(suffix)))
235 }
236
237 pub fn as_str(&self) -> &str {
239 &self.0
240 }
241
242 pub fn is_empty(&self) -> bool {
244 self.0.is_empty()
245 }
246
247 pub fn len(&self) -> usize {
249 self.0.len()
250 }
251
252 pub fn join<'a>(&self, other: impl Into<PathRef<'a>>) -> Path {
266 let other = other.into();
267 if self.0.is_empty() {
268 other.to_owned()
269 } else if other.is_empty() {
270 self.clone()
271 } else {
272 Path::new(format!("{}/{}", self.0, other.as_str()))
274 }
275 }
276}
277
278impl Display for Path {
279 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280 write!(f, "{}", self.0)
281 }
282}
283
284impl AsRef<str> for Path {
285 fn as_ref(&self) -> &str {
286 &self.0
287 }
288}
289
290impl From<String> for Path {
291 fn from(s: String) -> Self {
292 Self::new(&s)
293 }
294}
295
296impl From<&str> for Path {
297 fn from(s: &str) -> Self {
298 Self::new(s)
299 }
300}
301
302impl From<&String> for Path {
303 fn from(s: &String) -> Self {
304 Self::new(s)
305 }
306}
307
308impl From<&Path> for Path {
309 fn from(p: &Path) -> Self {
310 p.clone()
311 }
312}
313
314impl From<PathRef<'_>> for Path {
315 fn from(p: PathRef<'_>) -> Self {
316 Path(p.0.into_owned())
317 }
318}
319
320impl Decode for Path {
321 fn decode<R: bytes::Buf>(r: &mut R) -> Result<Self, DecodeError> {
322 let path = String::decode(r)?;
323 Ok(Self::new(&path))
324 }
325}
326
327impl Encode for Path {
328 fn encode<W: bytes::BufMut>(&self, w: &mut W) {
329 self.0.encode(w)
330 }
331}
332
333#[cfg(feature = "serde")]
334impl<'de> serde::Deserialize<'de> for Path {
335 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
336 where
337 D: serde::Deserializer<'de>,
338 {
339 let s = String::deserialize(deserializer)?;
340 Ok(Path::new(&s))
341 }
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347
348 #[test]
349 fn test_has_prefix() {
350 let path = Path::new("foo/bar/baz");
351
352 assert!(path.has_prefix(""));
354 assert!(path.has_prefix("foo"));
355 assert!(path.has_prefix(&Path::new("foo")));
356 assert!(path.has_prefix("foo/"));
357 assert!(path.has_prefix("foo/bar"));
358 assert!(path.has_prefix(&Path::new("foo/bar/")));
359 assert!(path.has_prefix("foo/bar/baz"));
360
361 assert!(!path.has_prefix("f"));
363 assert!(!path.has_prefix(&Path::new("fo")));
364 assert!(!path.has_prefix("foo/b"));
365 assert!(!path.has_prefix("foo/ba"));
366 assert!(!path.has_prefix(&Path::new("foo/bar/ba")));
367
368 let path = Path::new("foobar");
370 assert!(!path.has_prefix("foo"));
371 assert!(path.has_prefix(&Path::new("foobar")));
372 }
373
374 #[test]
375 fn test_strip_prefix() {
376 let path = Path::new("foo/bar/baz");
377
378 assert_eq!(path.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
380 assert_eq!(path.strip_prefix("foo").unwrap().as_str(), "bar/baz");
381 assert_eq!(path.strip_prefix(&Path::new("foo/")).unwrap().as_str(), "bar/baz");
382 assert_eq!(path.strip_prefix("foo/bar").unwrap().as_str(), "baz");
383 assert_eq!(path.strip_prefix(&Path::new("foo/bar/")).unwrap().as_str(), "baz");
384 assert_eq!(path.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
385
386 assert!(path.strip_prefix("fo").is_none());
388 assert!(path.strip_prefix(&Path::new("bar")).is_none());
389 }
390
391 #[test]
392 fn test_join() {
393 assert_eq!(Path::new("foo").join("bar").as_str(), "foo/bar");
395 assert_eq!(Path::new("foo/").join(&Path::new("bar")).as_str(), "foo/bar");
396 assert_eq!(Path::new("").join("bar").as_str(), "bar");
397 assert_eq!(Path::new("foo/bar").join(&Path::new("baz")).as_str(), "foo/bar/baz");
398 }
399
400 #[test]
401 fn test_empty() {
402 let empty = Path::new("");
403 assert!(empty.is_empty());
404 assert_eq!(empty.len(), 0);
405
406 let non_empty = Path::new("foo");
407 assert!(!non_empty.is_empty());
408 assert_eq!(non_empty.len(), 3);
409 }
410
411 #[test]
412 fn test_from_conversions() {
413 let path1 = Path::from("foo/bar");
414 let path2 = Path::from(String::from("foo/bar"));
415 let s = String::from("foo/bar");
416 let path3 = Path::from(&s);
417
418 assert_eq!(path1.as_str(), "foo/bar");
419 assert_eq!(path2.as_str(), "foo/bar");
420 assert_eq!(path3.as_str(), "foo/bar");
421 }
422
423 #[test]
424 fn test_path_prefix_join() {
425 let prefix = Path::new("foo");
426 let suffix = Path::new("bar/baz");
427 let path = prefix.join(&suffix);
428 assert_eq!(path.as_str(), "foo/bar/baz");
429
430 let prefix = Path::new("foo/");
431 let suffix = Path::new("bar/baz");
432 let path = prefix.join(&suffix);
433 assert_eq!(path.as_str(), "foo/bar/baz");
434
435 let prefix = Path::new("foo");
436 let suffix = Path::new("/bar/baz");
437 let path = prefix.join(&suffix);
438 assert_eq!(path.as_str(), "foo/bar/baz");
439
440 let prefix = Path::new("");
441 let suffix = Path::new("bar/baz");
442 let path = prefix.join(&suffix);
443 assert_eq!(path.as_str(), "bar/baz");
444 }
445
446 #[test]
447 fn test_path_prefix_conversions() {
448 let prefix1 = Path::from("foo/bar");
449 let prefix2 = Path::from(String::from("foo/bar"));
450 let s = String::from("foo/bar");
451 let prefix3 = Path::from(&s);
452
453 assert_eq!(prefix1.as_str(), "foo/bar");
454 assert_eq!(prefix2.as_str(), "foo/bar");
455 assert_eq!(prefix3.as_str(), "foo/bar");
456 }
457
458 #[test]
459 fn test_path_suffix_conversions() {
460 let suffix1 = Path::from("foo/bar");
461 let suffix2 = Path::from(String::from("foo/bar"));
462 let s = String::from("foo/bar");
463 let suffix3 = Path::from(&s);
464
465 assert_eq!(suffix1.as_str(), "foo/bar");
466 assert_eq!(suffix2.as_str(), "foo/bar");
467 assert_eq!(suffix3.as_str(), "foo/bar");
468 }
469
470 #[test]
471 fn test_path_types_basic_operations() {
472 let prefix = Path::new("foo/bar");
473 assert_eq!(prefix.as_str(), "foo/bar");
474 assert!(!prefix.is_empty());
475 assert_eq!(prefix.len(), 7);
476
477 let suffix = Path::new("baz/qux");
478 assert_eq!(suffix.as_str(), "baz/qux");
479 assert!(!suffix.is_empty());
480 assert_eq!(suffix.len(), 7);
481
482 let empty_prefix = Path::new("");
483 assert!(empty_prefix.is_empty());
484 assert_eq!(empty_prefix.len(), 0);
485
486 let empty_suffix = Path::new("");
487 assert!(empty_suffix.is_empty());
488 assert_eq!(empty_suffix.len(), 0);
489 }
490
491 #[test]
492 fn test_prefix_has_prefix() {
493 let prefix = Path::new("foo/bar");
495 assert!(prefix.has_prefix(&Path::new("")));
496
497 let prefix = Path::new("foo/bar");
499 assert!(prefix.has_prefix(&Path::new("foo/bar")));
500
501 assert!(prefix.has_prefix(&Path::new("foo")));
503 assert!(prefix.has_prefix(&Path::new("foo/")));
504
505 assert!(!prefix.has_prefix(&Path::new("f")));
507 assert!(!prefix.has_prefix(&Path::new("fo")));
508 assert!(!prefix.has_prefix(&Path::new("foo/b")));
509 assert!(!prefix.has_prefix(&Path::new("foo/ba")));
510
511 let prefix = Path::new("foobar");
513 assert!(!prefix.has_prefix(&Path::new("foo")));
514 assert!(prefix.has_prefix(&Path::new("foobar")));
515
516 let prefix = Path::new("foo/bar/");
518 assert!(prefix.has_prefix(&Path::new("foo")));
519 assert!(prefix.has_prefix(&Path::new("foo/")));
520 assert!(prefix.has_prefix(&Path::new("foo/bar")));
521 assert!(prefix.has_prefix(&Path::new("foo/bar/")));
522
523 let prefix = Path::new("foo");
525 assert!(prefix.has_prefix(&Path::new("")));
526 assert!(prefix.has_prefix(&Path::new("foo")));
527 assert!(prefix.has_prefix(&Path::new("foo/"))); assert!(!prefix.has_prefix(&Path::new("f")));
529
530 let prefix = Path::new("");
532 assert!(prefix.has_prefix(&Path::new("")));
533 assert!(!prefix.has_prefix(&Path::new("foo")));
534 }
535
536 #[test]
537 fn test_prefix_join() {
538 let prefix = Path::new("foo");
540 let suffix = Path::new("bar");
541 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar");
542
543 let prefix = Path::new("foo/");
545 let suffix = Path::new("bar");
546 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar");
547
548 let prefix = Path::new("foo");
550 let suffix = Path::new("/bar");
551 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar");
552
553 let prefix = Path::new("foo");
555 let suffix = Path::new("bar/");
556 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar"); let prefix = Path::new("foo/");
560 let suffix = Path::new("/bar");
561 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar");
562
563 let prefix = Path::new("foo");
565 let suffix = Path::new("");
566 assert_eq!(prefix.join(&suffix).as_str(), "foo");
567
568 let prefix = Path::new("");
570 let suffix = Path::new("bar");
571 assert_eq!(prefix.join(&suffix).as_str(), "bar");
572
573 let prefix = Path::new("");
575 let suffix = Path::new("");
576 assert_eq!(prefix.join(&suffix).as_str(), "");
577
578 let prefix = Path::new("foo/bar");
580 let suffix = Path::new("baz/qux");
581 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar/baz/qux");
582
583 let prefix = Path::new("foo/bar/");
585 let suffix = Path::new("/baz/qux/");
586 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar/baz/qux"); }
588
589 #[test]
590 fn test_path_ref() {
591 let ref1 = PathRef::new("/foo/bar/");
593 assert_eq!(ref1.as_str(), "foo/bar");
594
595 let ref2 = PathRef::from("///foo///");
596 assert_eq!(ref2.as_str(), "foo");
597
598 let ref3 = PathRef::new("foo//bar///baz");
600 assert_eq!(ref3.as_str(), "foo/bar/baz");
601
602 let path = Path::new("foo/bar");
604 let path_ref = PathRef::from(&path);
605 assert_eq!(path_ref.as_str(), "foo/bar");
606
607 let path2 = Path::new("foo/bar/baz");
609 assert!(path2.has_prefix(&path_ref));
610 assert_eq!(path2.strip_prefix(&path_ref).unwrap().as_str(), "baz");
611
612 let empty = PathRef::new("");
614 assert!(empty.is_empty());
615 assert_eq!(empty.len(), 0);
616 }
617
618 #[test]
619 fn test_multiple_consecutive_slashes() {
620 let path = Path::new("foo//bar///baz");
621 assert_eq!(path.as_str(), "foo/bar/baz");
623
624 let path2 = Path::new("//foo//bar///baz//");
626 assert_eq!(path2.as_str(), "foo/bar/baz");
627
628 let path3 = Path::new("foo///bar");
630 assert_eq!(path3.as_str(), "foo/bar");
631 }
632
633 #[test]
634 fn test_removes_multiple_slashes_comprehensively() {
635 assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
637 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
638 assert_eq!(Path::new("foo////bar").as_str(), "foo/bar");
639
640 assert_eq!(Path::new("foo//bar//baz").as_str(), "foo/bar/baz");
642 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
643
644 assert_eq!(Path::new("foo//bar///baz////qux").as_str(), "foo/bar/baz/qux");
646
647 assert_eq!(Path::new("//foo//bar//").as_str(), "foo/bar");
649 assert_eq!(Path::new("///foo///bar///").as_str(), "foo/bar");
650
651 assert_eq!(Path::new("//").as_str(), "");
653 assert_eq!(Path::new("////").as_str(), "");
654
655 let path_with_slashes = Path::new("foo//bar///baz");
657 assert!(path_with_slashes.has_prefix("foo/bar"));
658 assert_eq!(path_with_slashes.strip_prefix("foo").unwrap().as_str(), "bar/baz");
659 assert_eq!(path_with_slashes.join("qux").as_str(), "foo/bar/baz/qux");
660
661 let path_ref = PathRef::new("foo//bar///baz");
663 assert_eq!(path_ref.as_str(), "foo/bar/baz"); let path_from_ref = path_ref.to_owned();
665 assert_eq!(path_from_ref.as_str(), "foo/bar/baz"); }
667
668 #[test]
669 fn test_path_ref_multiple_slashes() {
670 let path_ref = PathRef::new("//foo//bar///baz//");
672 assert_eq!(path_ref.as_str(), "foo/bar/baz"); assert_eq!(PathRef::new("foo//bar").as_str(), "foo/bar");
676 assert_eq!(PathRef::new("foo///bar").as_str(), "foo/bar");
677 assert_eq!(PathRef::new("a//b//c//d").as_str(), "a/b/c/d");
678
679 assert_eq!(PathRef::new("foo//bar").to_owned().as_str(), "foo/bar");
681 assert_eq!(PathRef::new("foo///bar").to_owned().as_str(), "foo/bar");
682 assert_eq!(PathRef::new("a//b//c//d").to_owned().as_str(), "a/b/c/d");
683
684 assert_eq!(PathRef::new("//").as_str(), "");
686 assert_eq!(PathRef::new("////").as_str(), "");
687 assert_eq!(PathRef::new("//").to_owned().as_str(), "");
688 assert_eq!(PathRef::new("////").to_owned().as_str(), "");
689
690 let normal_path = PathRef::new("foo/bar/baz");
692 assert_eq!(normal_path.as_str(), "foo/bar/baz");
693 let needs_norm = PathRef::new("foo//bar");
696 assert_eq!(needs_norm.as_str(), "foo/bar");
697 }
699
700 #[test]
701 fn test_ergonomic_conversions() {
702 fn takes_path_ref<'a>(p: impl Into<PathRef<'a>>) -> String {
704 p.into().as_str().to_string()
705 }
706
707 fn takes_path_ref_with_trait<'a>(p: impl IntoPathRef<'a>) -> String {
709 p.into().as_str().to_string()
710 }
711
712 assert_eq!(takes_path_ref("foo//bar"), "foo/bar");
714
715 let owned_string = String::from("foo//bar///baz");
717 assert_eq!(takes_path_ref(owned_string), "foo/bar/baz");
718
719 let string_ref = String::from("foo//bar");
721 assert_eq!(takes_path_ref(&string_ref), "foo/bar");
722
723 let path_ref = PathRef::new("foo//bar");
725 assert_eq!(takes_path_ref(&path_ref), "foo/bar");
726
727 let path = Path::new("foo//bar");
729 assert_eq!(takes_path_ref(&path), "foo/bar");
730
731 let _path1 = Path::new("foo/bar"); let _path2 = Path::new(String::from("foo/bar")); let _path3 = Path::new(String::from("foo/bar")); let _path4 = Path::new(PathRef::new("foo/bar")); assert_eq!(takes_path_ref_with_trait("foo//bar"), "foo/bar");
739 assert_eq!(takes_path_ref_with_trait(String::from("foo//bar")), "foo/bar");
740 }
741
742 #[test]
743 fn test_prefix_strip_prefix() {
744 let prefix = Path::new("foo/bar/baz");
746 assert_eq!(prefix.strip_prefix(&Path::new("")).unwrap().as_str(), "foo/bar/baz");
747 assert_eq!(prefix.strip_prefix(&Path::new("foo")).unwrap().as_str(), "bar/baz");
748 assert_eq!(prefix.strip_prefix(&Path::new("foo/")).unwrap().as_str(), "bar/baz");
749 assert_eq!(prefix.strip_prefix(&Path::new("foo/bar")).unwrap().as_str(), "baz");
750 assert_eq!(prefix.strip_prefix(&Path::new("foo/bar/")).unwrap().as_str(), "baz");
751 assert_eq!(prefix.strip_prefix(&Path::new("foo/bar/baz")).unwrap().as_str(), "");
752
753 assert!(prefix.strip_prefix(&Path::new("fo")).is_none());
755 assert!(prefix.strip_prefix(&Path::new("bar")).is_none());
756 assert!(prefix.strip_prefix(&Path::new("foo/ba")).is_none());
757
758 let prefix = Path::new("foobar");
760 assert!(prefix.strip_prefix(&Path::new("foo")).is_none());
761 assert_eq!(prefix.strip_prefix(&Path::new("foobar")).unwrap().as_str(), "");
762
763 let prefix = Path::new("");
765 assert_eq!(prefix.strip_prefix(&Path::new("")).unwrap().as_str(), "");
766 assert!(prefix.strip_prefix(&Path::new("foo")).is_none());
767
768 let prefix = Path::new("foo");
770 assert_eq!(prefix.strip_prefix(&Path::new("foo")).unwrap().as_str(), "");
771 assert_eq!(prefix.strip_prefix(&Path::new("foo/")).unwrap().as_str(), ""); let prefix = Path::new("foo/bar/");
775 assert_eq!(prefix.strip_prefix(&Path::new("foo")).unwrap().as_str(), "bar");
776 assert_eq!(prefix.strip_prefix(&Path::new("foo/")).unwrap().as_str(), "bar");
777 assert_eq!(prefix.strip_prefix(&Path::new("foo/bar")).unwrap().as_str(), "");
778 assert_eq!(prefix.strip_prefix(&Path::new("foo/bar/")).unwrap().as_str(), "");
779 }
780}