1use std::borrow::Cow;
2use std::fmt::{self, Display};
3use std::sync::Arc;
4
5use crate::coding::{Decode, DecodeError, Encode, EncodeError};
6
7pub type PathOwned = Path<'static>;
9
10pub trait AsPath {
15 fn as_path(&self) -> Path<'_>;
17}
18
19impl<'a> AsPath for &'a str {
20 fn as_path(&self) -> Path<'a> {
21 Path::new(self)
22 }
23}
24
25impl<'a> AsPath for &'a Path<'a> {
26 fn as_path(&self) -> Path<'a> {
27 self.borrow()
29 }
30}
31
32impl AsPath for Path<'_> {
33 fn as_path(&self) -> Path<'_> {
34 self.borrow()
35 }
36}
37
38impl AsPath for String {
39 fn as_path(&self) -> Path<'_> {
40 Path::new(self)
41 }
42}
43
44impl<'a> AsPath for &'a String {
45 fn as_path(&self) -> Path<'a> {
46 Path::new(self)
47 }
48}
49
50#[derive(Clone)]
56enum Repr<'a> {
57 Borrowed(&'a str),
58 Shared { buf: Arc<str>, start: usize },
59}
60
61#[derive(Clone)]
92pub struct Path<'a>(Repr<'a>);
93
94impl<'a> Path<'a> {
95 pub const MAX_PARTS: usize = 32;
101
102 pub fn new(s: &'a str) -> Self {
107 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
108
109 if trimmed.contains("//") {
111 let normalized = trimmed
113 .split('/')
114 .filter(|s| !s.is_empty())
115 .collect::<Vec<_>>()
116 .join("/");
117 Self(Repr::Shared {
118 buf: normalized.into(),
119 start: 0,
120 })
121 } else {
122 Self(Repr::Borrowed(trimmed))
124 }
125 }
126
127 fn slice_from(&'a self, n: usize) -> Path<'a> {
129 match &self.0 {
130 Repr::Borrowed(s) => Path(Repr::Borrowed(&s[n..])),
131 Repr::Shared { buf, start } => Path(Repr::Shared {
132 buf: buf.clone(),
133 start: start + n,
134 }),
135 }
136 }
137
138 pub fn has_prefix(&self, prefix: impl AsPath) -> bool {
160 let prefix = prefix.as_path();
161
162 if prefix.is_empty() {
163 return true;
164 }
165
166 let s = self.as_str();
167 if !s.starts_with(prefix.as_str()) {
168 return false;
169 }
170
171 if s.len() == prefix.len() {
173 return true;
174 }
175
176 s.as_bytes().get(prefix.len()) == Some(&b'/')
178 }
179
180 pub fn strip_prefix(&'a self, prefix: impl AsPath) -> Option<Path<'a>> {
185 let prefix = prefix.as_path();
186
187 if prefix.is_empty() {
188 return Some(self.borrow());
189 }
190
191 let s = self.as_str();
192 if !s.starts_with(prefix.as_str()) {
193 return None;
194 }
195
196 if s.len() == prefix.len() {
198 return Some(Path::empty());
199 }
200
201 if s.as_bytes().get(prefix.len()) != Some(&b'/') {
203 return None;
204 }
205
206 Some(self.slice_from(prefix.len() + 1))
207 }
208
209 pub fn parts(&self) -> impl Iterator<Item = &str> {
222 self.as_str().split('/').filter(|part| !part.is_empty())
225 }
226
227 pub fn next_part(&'a self) -> Option<(&'a str, Path<'a>)> {
229 let s = self.as_str();
230 if s.is_empty() {
231 return None;
232 }
233
234 if let Some(i) = s.find('/') {
235 Some((&s[..i], self.slice_from(i + 1)))
236 } else {
237 Some((s, Path::empty()))
238 }
239 }
240
241 pub fn as_str(&self) -> &str {
243 match &self.0 {
244 Repr::Borrowed(s) => s,
245 Repr::Shared { buf, start } => &buf[*start..],
246 }
247 }
248
249 pub fn empty() -> Path<'static> {
251 Path(Repr::Borrowed(""))
252 }
253
254 pub fn is_empty(&self) -> bool {
256 self.as_str().is_empty()
257 }
258
259 pub fn len(&self) -> usize {
261 self.as_str().len()
262 }
263
264 pub fn to_owned(&self) -> PathOwned {
266 match &self.0 {
267 Repr::Borrowed("") => Path::empty(),
268 Repr::Borrowed(s) => Path(Repr::Shared {
269 buf: Arc::from(*s),
270 start: 0,
271 }),
272 Repr::Shared { buf, start } => Path(Repr::Shared {
273 buf: buf.clone(),
274 start: *start,
275 }),
276 }
277 }
278
279 pub fn into_owned(self) -> PathOwned {
281 match self.0 {
282 Repr::Borrowed("") => Path::empty(),
283 Repr::Borrowed(s) => Path(Repr::Shared {
284 buf: Arc::from(s),
285 start: 0,
286 }),
287 Repr::Shared { buf, start } => Path(Repr::Shared { buf, start }),
288 }
289 }
290
291 pub fn borrow(&'a self) -> Path<'a> {
293 self.slice_from(0)
294 }
295
296 pub fn join(&self, other: impl AsPath) -> PathOwned {
310 let other = other.as_path();
311
312 if self.is_empty() {
313 other.to_owned()
314 } else if other.is_empty() {
315 self.to_owned()
316 } else {
317 Path(Repr::Shared {
319 buf: format!("{}/{}", self.as_str(), other.as_str()).into(),
320 start: 0,
321 })
322 }
323 }
324
325 pub fn resolve(&self, rel: &PathRelative<'_>) -> PathOwned {
345 if rel.is_empty() {
346 return self.to_owned();
347 }
348
349 let mut segments: Vec<&str> = self.parts().collect();
350 segments.pop();
351
352 for seg in rel.as_str().split('/') {
353 if seg == "." {
354 continue;
355 } else if seg == ".." {
356 segments.pop();
357 } else {
358 segments.push(seg);
359 }
360 }
361
362 let path = segments.join("/");
363 if path.is_empty() {
364 Path::empty()
365 } else {
366 Path(Repr::Shared {
367 buf: path.into(),
368 start: 0,
369 })
370 }
371 }
372
373 pub fn try_resolve(&self, rel: &PathRelative<'_>) -> Option<PathOwned> {
379 if rel.is_empty() {
380 return Some(self.to_owned());
381 }
382
383 let mut segments: Vec<&str> = self.parts().collect();
384 segments.pop();
385
386 for seg in rel.as_str().split('/') {
387 if seg == "." {
388 continue;
389 } else if seg == ".." {
390 segments.pop()?;
391 } else {
392 segments.push(seg);
393 }
394 }
395
396 let path = segments.join("/");
397 if path.is_empty() {
398 Some(Path::empty())
399 } else {
400 Some(Path(Repr::Shared {
401 buf: path.into(),
402 start: 0,
403 }))
404 }
405 }
406}
407
408impl<'b> PartialEq<Path<'b>> for Path<'_> {
411 fn eq(&self, other: &Path<'b>) -> bool {
412 self.as_str() == other.as_str()
413 }
414}
415
416impl Eq for Path<'_> {}
417
418impl PartialOrd for Path<'_> {
419 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
420 Some(self.cmp(other))
421 }
422}
423
424impl Ord for Path<'_> {
425 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
426 self.as_str().cmp(other.as_str())
427 }
428}
429
430impl std::hash::Hash for Path<'_> {
431 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
432 self.as_str().hash(state)
433 }
434}
435
436impl fmt::Debug for Path<'_> {
437 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438 f.debug_tuple("Path").field(&self.as_str()).finish()
439 }
440}
441
442impl serde::Serialize for Path<'_> {
443 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
444 serializer.serialize_str(self.as_str())
445 }
446}
447
448impl<'a> From<&'a str> for Path<'a> {
449 fn from(s: &'a str) -> Self {
450 Self::new(s)
451 }
452}
453
454impl<'a> From<&'a String> for Path<'a> {
455 fn from(s: &'a String) -> Self {
456 Self::new(s)
458 }
459}
460
461impl Default for Path<'_> {
462 fn default() -> Self {
463 Path::empty()
464 }
465}
466
467impl From<String> for Path<'_> {
468 fn from(s: String) -> Self {
469 Path::new(&s).into_owned()
470 }
471}
472
473impl AsRef<str> for Path<'_> {
474 fn as_ref(&self) -> &str {
475 self.as_str()
476 }
477}
478
479impl Display for Path<'_> {
480 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
481 write!(f, "{}", self.as_str())
482 }
483}
484
485impl<V: Copy> Decode<V> for Path<'_>
486where
487 String: Decode<V>,
488{
489 fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
490 let path: Path = String::decode(r, version)?.into();
491 if path.parts().count() > Path::MAX_PARTS {
492 return Err(DecodeError::BoundsExceeded);
493 }
494 Ok(path)
495 }
496}
497
498impl<V: Copy> Encode<V> for Path<'_>
499where
500 for<'a> &'a str: Encode<V>,
501{
502 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
503 if self.parts().count() > Path::MAX_PARTS {
504 return Err(EncodeError::BoundsExceeded);
505 }
506 self.as_str().encode(w, version)?;
507 Ok(())
508 }
509}
510
511pub type PathRelativeOwned = PathRelative<'static>;
513
514#[derive(Debug, PartialEq, Eq, Hash, Clone, serde::Serialize)]
542pub struct PathRelative<'a>(Cow<'a, str>);
543
544impl<'a> PathRelative<'a> {
545 pub fn new(s: &'a str) -> Self {
550 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
551
552 if needs_normalize_relative(trimmed) {
553 Self(Cow::Owned(normalize_relative_segments(trimmed)))
554 } else {
555 Self(Cow::Borrowed(trimmed))
556 }
557 }
558
559 pub fn as_str(&self) -> &str {
561 &self.0
562 }
563
564 pub fn empty() -> PathRelative<'static> {
566 PathRelative(Cow::Borrowed(""))
567 }
568
569 pub fn is_empty(&self) -> bool {
571 self.0.is_empty()
572 }
573
574 pub fn len(&self) -> usize {
576 self.0.len()
577 }
578
579 pub fn to_owned(&self) -> PathRelativeOwned {
581 PathRelative(Cow::Owned(self.0.to_string()))
582 }
583
584 pub fn into_owned(self) -> PathRelativeOwned {
586 PathRelative(Cow::Owned(self.0.into_owned()))
587 }
588
589 pub fn borrow(&'a self) -> PathRelative<'a> {
591 PathRelative(Cow::Borrowed(&self.0))
592 }
593}
594
595impl<'a> From<&'a str> for PathRelative<'a> {
596 fn from(s: &'a str) -> Self {
597 Self::new(s)
598 }
599}
600
601impl<'a> From<&'a String> for PathRelative<'a> {
602 fn from(s: &'a String) -> Self {
603 Self::new(s)
604 }
605}
606
607impl From<String> for PathRelative<'_> {
608 fn from(s: String) -> Self {
609 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
610
611 if needs_normalize_relative(trimmed) {
612 Self(Cow::Owned(normalize_relative_segments(trimmed)))
613 } else if trimmed == s {
614 Self(Cow::Owned(s))
615 } else {
616 Self(Cow::Owned(trimmed.to_string()))
617 }
618 }
619}
620
621fn needs_normalize_relative(trimmed: &str) -> bool {
622 trimmed.split('/').any(|seg| seg.is_empty() || seg == ".")
623}
624
625fn normalize_relative_segments(trimmed: &str) -> String {
626 let segments = trimmed
627 .split('/')
628 .filter(|seg| !seg.is_empty() && *seg != ".")
629 .collect::<Vec<_>>()
630 .join("/");
631
632 if segments.is_empty() && trimmed.split('/').any(|seg| seg == ".") {
633 ".".to_string()
634 } else {
635 segments
636 }
637}
638
639impl Default for PathRelative<'_> {
640 fn default() -> Self {
641 Self(Cow::Borrowed(""))
642 }
643}
644
645impl AsRef<str> for PathRelative<'_> {
646 fn as_ref(&self) -> &str {
647 &self.0
648 }
649}
650
651impl Display for PathRelative<'_> {
652 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
653 write!(f, "{}", self.0)
654 }
655}
656
657impl<'de> serde::Deserialize<'de> for PathRelative<'static> {
661 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
662 where
663 D: serde::Deserializer<'de>,
664 {
665 let s = String::deserialize(deserializer)?;
666 Ok(PathRelative::from(s))
667 }
668}
669
670#[derive(Debug, Clone, Default, Eq)]
676pub struct PathPrefixes {
677 paths: Vec<PathOwned>,
678}
679
680impl PathPrefixes {
681 pub fn new(paths: impl IntoIterator<Item = impl AsPath>) -> Self {
693 let mut paths: Vec<PathOwned> = paths.into_iter().map(|p| p.as_path().to_owned()).collect();
694
695 if paths.len() <= 1 {
696 return Self { paths };
697 }
698
699 paths.sort_by(|a, b| a.len().cmp(&b.len()).then_with(|| a.as_str().cmp(b.as_str())));
702 paths.dedup();
703
704 let mut result: Vec<PathOwned> = Vec::new();
705 'outer: for path in paths {
706 for existing in &result {
707 if path.has_prefix(existing) {
708 continue 'outer;
709 }
710 }
711 result.push(path);
712 }
713
714 Self { paths: result }
715 }
716
717 pub fn is_empty(&self) -> bool {
719 self.paths.is_empty()
720 }
721
722 pub fn len(&self) -> usize {
724 self.paths.len()
725 }
726
727 pub fn iter(&self) -> std::slice::Iter<'_, PathOwned> {
729 self.paths.iter()
730 }
731}
732
733impl std::ops::Deref for PathPrefixes {
734 type Target = [PathOwned];
735
736 fn deref(&self) -> &[PathOwned] {
737 &self.paths
738 }
739}
740
741impl FromIterator<PathOwned> for PathPrefixes {
742 fn from_iter<I: IntoIterator<Item = PathOwned>>(iter: I) -> Self {
743 Self::new(iter)
744 }
745}
746
747impl From<Vec<PathOwned>> for PathPrefixes {
748 fn from(paths: Vec<PathOwned>) -> Self {
749 Self::new(paths)
750 }
751}
752
753impl<'a> PartialEq<Vec<Path<'a>>> for PathPrefixes {
754 fn eq(&self, other: &Vec<Path<'a>>) -> bool {
755 self.paths == *other
756 }
757}
758
759impl<'a> PartialEq<PathPrefixes> for Vec<Path<'a>> {
760 fn eq(&self, other: &PathPrefixes) -> bool {
761 *self == other.paths
762 }
763}
764
765impl PartialEq for PathPrefixes {
766 fn eq(&self, other: &Self) -> bool {
767 self.paths == other.paths
768 }
769}
770
771impl IntoIterator for PathPrefixes {
772 type Item = PathOwned;
773 type IntoIter = std::vec::IntoIter<PathOwned>;
774
775 fn into_iter(self) -> Self::IntoIter {
776 self.paths.into_iter()
777 }
778}
779
780impl<'a> IntoIterator for &'a PathPrefixes {
781 type Item = &'a PathOwned;
782 type IntoIter = std::slice::Iter<'a, PathOwned>;
783
784 fn into_iter(self) -> Self::IntoIter {
785 self.paths.iter()
786 }
787}
788
789#[cfg(test)]
790mod tests {
791 use super::*;
792
793 #[test]
794 fn test_has_prefix() {
795 let path = Path::new("foo/bar/baz");
796
797 assert!(path.has_prefix(""));
799 assert!(path.has_prefix("foo"));
800 assert!(path.has_prefix(Path::new("foo")));
801 assert!(path.has_prefix("foo/"));
802 assert!(path.has_prefix("foo/bar"));
803 assert!(path.has_prefix(Path::new("foo/bar/")));
804 assert!(path.has_prefix("foo/bar/baz"));
805
806 assert!(!path.has_prefix("f"));
808 assert!(!path.has_prefix(Path::new("fo")));
809 assert!(!path.has_prefix("foo/b"));
810 assert!(!path.has_prefix("foo/ba"));
811 assert!(!path.has_prefix(Path::new("foo/bar/ba")));
812
813 let path = Path::new("foobar");
815 assert!(!path.has_prefix("foo"));
816 assert!(path.has_prefix(Path::new("foobar")));
817 }
818
819 #[test]
820 fn test_strip_prefix() {
821 let path = Path::new("foo/bar/baz");
822
823 assert_eq!(path.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
825 assert_eq!(path.strip_prefix("foo").unwrap().as_str(), "bar/baz");
826 assert_eq!(path.strip_prefix(Path::new("foo/")).unwrap().as_str(), "bar/baz");
827 assert_eq!(path.strip_prefix("foo/bar").unwrap().as_str(), "baz");
828 assert_eq!(path.strip_prefix(Path::new("foo/bar/")).unwrap().as_str(), "baz");
829 assert_eq!(path.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
830
831 assert!(path.strip_prefix("fo").is_none());
833 assert!(path.strip_prefix(Path::new("bar")).is_none());
834 }
835
836 #[test]
837 fn test_join() {
838 assert_eq!(Path::new("foo").join("bar").as_str(), "foo/bar");
840 assert_eq!(Path::new("foo/").join(Path::new("bar")).as_str(), "foo/bar");
841 assert_eq!(Path::new("").join("bar").as_str(), "bar");
842 assert_eq!(Path::new("foo/bar").join(Path::new("baz")).as_str(), "foo/bar/baz");
843 }
844
845 #[test]
846 fn test_empty() {
847 let empty = Path::new("");
848 assert!(empty.is_empty());
849 assert_eq!(empty.len(), 0);
850
851 let non_empty = Path::new("foo");
852 assert!(!non_empty.is_empty());
853 assert_eq!(non_empty.len(), 3);
854 }
855
856 #[test]
857 fn test_from_conversions() {
858 let path1 = Path::from("foo/bar");
859 let path2 = Path::from("foo/bar");
860 let s = String::from("foo/bar");
861 let path3 = Path::from(&s);
862
863 assert_eq!(path1.as_str(), "foo/bar");
864 assert_eq!(path2.as_str(), "foo/bar");
865 assert_eq!(path3.as_str(), "foo/bar");
866 }
867
868 #[test]
869 fn test_path_prefix_join() {
870 let prefix = Path::new("foo");
871 let suffix = Path::new("bar/baz");
872 let path = prefix.join(&suffix);
873 assert_eq!(path.as_str(), "foo/bar/baz");
874
875 let prefix = Path::new("foo/");
876 let suffix = Path::new("bar/baz");
877 let path = prefix.join(&suffix);
878 assert_eq!(path.as_str(), "foo/bar/baz");
879
880 let prefix = Path::new("foo");
881 let suffix = Path::new("/bar/baz");
882 let path = prefix.join(&suffix);
883 assert_eq!(path.as_str(), "foo/bar/baz");
884
885 let prefix = Path::new("");
886 let suffix = Path::new("bar/baz");
887 let path = prefix.join(&suffix);
888 assert_eq!(path.as_str(), "bar/baz");
889 }
890
891 #[test]
892 fn test_path_prefix_conversions() {
893 let prefix1 = Path::from("foo/bar");
894 let prefix2 = Path::from(String::from("foo/bar"));
895 let s = String::from("foo/bar");
896 let prefix3 = Path::from(&s);
897
898 assert_eq!(prefix1.as_str(), "foo/bar");
899 assert_eq!(prefix2.as_str(), "foo/bar");
900 assert_eq!(prefix3.as_str(), "foo/bar");
901 }
902
903 #[test]
904 fn test_path_suffix_conversions() {
905 let suffix1 = Path::from("foo/bar");
906 let suffix2 = Path::from(String::from("foo/bar"));
907 let s = String::from("foo/bar");
908 let suffix3 = Path::from(&s);
909
910 assert_eq!(suffix1.as_str(), "foo/bar");
911 assert_eq!(suffix2.as_str(), "foo/bar");
912 assert_eq!(suffix3.as_str(), "foo/bar");
913 }
914
915 #[test]
916 fn test_path_types_basic_operations() {
917 let prefix = Path::new("foo/bar");
918 assert_eq!(prefix.as_str(), "foo/bar");
919 assert!(!prefix.is_empty());
920 assert_eq!(prefix.len(), 7);
921
922 let suffix = Path::new("baz/qux");
923 assert_eq!(suffix.as_str(), "baz/qux");
924 assert!(!suffix.is_empty());
925 assert_eq!(suffix.len(), 7);
926
927 let empty_prefix = Path::new("");
928 assert!(empty_prefix.is_empty());
929 assert_eq!(empty_prefix.len(), 0);
930
931 let empty_suffix = Path::new("");
932 assert!(empty_suffix.is_empty());
933 assert_eq!(empty_suffix.len(), 0);
934 }
935
936 #[test]
937 fn test_prefix_has_prefix() {
938 let prefix = Path::new("foo/bar");
940 assert!(prefix.has_prefix(""));
941
942 let prefix = Path::new("foo/bar");
944 assert!(prefix.has_prefix("foo/bar"));
945
946 assert!(prefix.has_prefix("foo"));
948 assert!(prefix.has_prefix("foo/"));
949
950 assert!(!prefix.has_prefix("f"));
952 assert!(!prefix.has_prefix("fo"));
953 assert!(!prefix.has_prefix("foo/b"));
954 assert!(!prefix.has_prefix("foo/ba"));
955
956 let prefix = Path::new("foobar");
958 assert!(!prefix.has_prefix("foo"));
959 assert!(prefix.has_prefix("foobar"));
960
961 let prefix = Path::new("foo/bar/");
963 assert!(prefix.has_prefix("foo"));
964 assert!(prefix.has_prefix("foo/"));
965 assert!(prefix.has_prefix("foo/bar"));
966 assert!(prefix.has_prefix("foo/bar/"));
967
968 let prefix = Path::new("foo");
970 assert!(prefix.has_prefix(""));
971 assert!(prefix.has_prefix("foo"));
972 assert!(prefix.has_prefix("foo/")); assert!(!prefix.has_prefix("f"));
974
975 let prefix = Path::new("");
977 assert!(prefix.has_prefix(""));
978 assert!(!prefix.has_prefix("foo"));
979 }
980
981 #[test]
982 fn test_prefix_join() {
983 let prefix = Path::new("foo");
985 let suffix = Path::new("bar");
986 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
987
988 let prefix = Path::new("foo/");
990 let suffix = Path::new("bar");
991 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
992
993 let prefix = Path::new("foo");
995 let suffix = Path::new("/bar");
996 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
997
998 let prefix = Path::new("foo");
1000 let suffix = Path::new("bar/");
1001 assert_eq!(prefix.join(suffix).as_str(), "foo/bar"); let prefix = Path::new("foo/");
1005 let suffix = Path::new("/bar");
1006 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
1007
1008 let prefix = Path::new("foo");
1010 let suffix = Path::new("");
1011 assert_eq!(prefix.join(suffix).as_str(), "foo");
1012
1013 let prefix = Path::new("");
1015 let suffix = Path::new("bar");
1016 assert_eq!(prefix.join(suffix).as_str(), "bar");
1017
1018 let prefix = Path::new("");
1020 let suffix = Path::new("");
1021 assert_eq!(prefix.join(suffix).as_str(), "");
1022
1023 let prefix = Path::new("foo/bar");
1025 let suffix = Path::new("baz/qux");
1026 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux");
1027
1028 let prefix = Path::new("foo/bar/");
1030 let suffix = Path::new("/baz/qux/");
1031 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux"); }
1033
1034 #[test]
1035 fn test_path_ref() {
1036 let ref1 = Path::new("/foo/bar/");
1038 assert_eq!(ref1.as_str(), "foo/bar");
1039
1040 let ref2 = Path::from("///foo///");
1041 assert_eq!(ref2.as_str(), "foo");
1042
1043 let ref3 = Path::new("foo//bar///baz");
1045 assert_eq!(ref3.as_str(), "foo/bar/baz");
1046
1047 let path = Path::new("foo/bar");
1049 let path_ref = path;
1050 assert_eq!(path_ref.as_str(), "foo/bar");
1051
1052 let path2 = Path::new("foo/bar/baz");
1054 assert!(path2.has_prefix(&path_ref));
1055 assert_eq!(path2.strip_prefix(path_ref).unwrap().as_str(), "baz");
1056
1057 let empty = Path::new("");
1059 assert!(empty.is_empty());
1060 assert_eq!(empty.len(), 0);
1061 }
1062
1063 #[test]
1064 fn test_multiple_consecutive_slashes() {
1065 let path = Path::new("foo//bar///baz");
1066 assert_eq!(path.as_str(), "foo/bar/baz");
1068
1069 let path2 = Path::new("//foo//bar///baz//");
1071 assert_eq!(path2.as_str(), "foo/bar/baz");
1072
1073 let path3 = Path::new("foo///bar");
1075 assert_eq!(path3.as_str(), "foo/bar");
1076 }
1077
1078 #[test]
1079 fn test_removes_multiple_slashes_comprehensively() {
1080 assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
1082 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
1083 assert_eq!(Path::new("foo////bar").as_str(), "foo/bar");
1084
1085 assert_eq!(Path::new("foo//bar//baz").as_str(), "foo/bar/baz");
1087 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
1088
1089 assert_eq!(Path::new("foo//bar///baz////qux").as_str(), "foo/bar/baz/qux");
1091
1092 assert_eq!(Path::new("//foo//bar//").as_str(), "foo/bar");
1094 assert_eq!(Path::new("///foo///bar///").as_str(), "foo/bar");
1095
1096 assert_eq!(Path::new("//").as_str(), "");
1098 assert_eq!(Path::new("////").as_str(), "");
1099
1100 let path_with_slashes = Path::new("foo//bar///baz");
1102 assert!(path_with_slashes.has_prefix("foo/bar"));
1103 assert_eq!(path_with_slashes.strip_prefix("foo").unwrap().as_str(), "bar/baz");
1104 assert_eq!(path_with_slashes.join("qux").as_str(), "foo/bar/baz/qux");
1105
1106 let path_ref = Path::new("foo//bar///baz");
1108 assert_eq!(path_ref.as_str(), "foo/bar/baz"); let path_from_ref = path_ref.to_owned();
1110 assert_eq!(path_from_ref.as_str(), "foo/bar/baz"); }
1112
1113 #[test]
1114 fn test_path_ref_multiple_slashes() {
1115 let path_ref = Path::new("//foo//bar///baz//");
1117 assert_eq!(path_ref.as_str(), "foo/bar/baz"); assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
1121 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
1122 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
1123
1124 assert_eq!(Path::new("foo//bar").to_owned().as_str(), "foo/bar");
1126 assert_eq!(Path::new("foo///bar").to_owned().as_str(), "foo/bar");
1127 assert_eq!(Path::new("a//b//c//d").to_owned().as_str(), "a/b/c/d");
1128
1129 assert_eq!(Path::new("//").as_str(), "");
1131 assert_eq!(Path::new("////").as_str(), "");
1132 assert_eq!(Path::new("//").to_owned().as_str(), "");
1133 assert_eq!(Path::new("////").to_owned().as_str(), "");
1134
1135 let normal_path = Path::new("foo/bar/baz");
1137 assert_eq!(normal_path.as_str(), "foo/bar/baz");
1138 let needs_norm = Path::new("foo//bar");
1141 assert_eq!(needs_norm.as_str(), "foo/bar");
1142 }
1144
1145 #[test]
1146 fn test_ergonomic_conversions() {
1147 fn takes_path_ref<'a>(p: impl Into<Path<'a>>) -> String {
1149 p.into().as_str().to_string()
1150 }
1151
1152 fn takes_path_ref_with_trait<'a>(p: impl Into<Path<'a>>) -> String {
1154 p.into().as_str().to_string()
1155 }
1156
1157 assert_eq!(takes_path_ref("foo//bar"), "foo/bar");
1159
1160 let owned_string = String::from("foo//bar///baz");
1162 assert_eq!(takes_path_ref(owned_string), "foo/bar/baz");
1163
1164 let string_ref = String::from("foo//bar");
1166 assert_eq!(takes_path_ref(string_ref), "foo/bar");
1167
1168 let path_ref = Path::new("foo//bar");
1170 assert_eq!(takes_path_ref(path_ref), "foo/bar");
1171
1172 let path = Path::new("foo//bar");
1174 assert_eq!(takes_path_ref(path), "foo/bar");
1175
1176 let _path1 = Path::new("foo/bar"); let _path2 = Path::new("foo/bar"); let _path3 = Path::new("foo/bar"); let _path4 = Path::new("foo/bar"); assert_eq!(takes_path_ref_with_trait("foo//bar"), "foo/bar");
1184 assert_eq!(takes_path_ref_with_trait(String::from("foo//bar")), "foo/bar");
1185 }
1186
1187 #[test]
1188 fn test_prefix_strip_prefix() {
1189 let prefix = Path::new("foo/bar/baz");
1191 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
1192 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar/baz");
1193 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar/baz");
1194 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "baz");
1195 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "baz");
1196 assert_eq!(prefix.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
1197
1198 assert!(prefix.strip_prefix("fo").is_none());
1200 assert!(prefix.strip_prefix("bar").is_none());
1201 assert!(prefix.strip_prefix("foo/ba").is_none());
1202
1203 let prefix = Path::new("foobar");
1205 assert!(prefix.strip_prefix("foo").is_none());
1206 assert_eq!(prefix.strip_prefix("foobar").unwrap().as_str(), "");
1207
1208 let prefix = Path::new("");
1210 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "");
1211 assert!(prefix.strip_prefix("foo").is_none());
1212
1213 let prefix = Path::new("foo");
1215 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "");
1216 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), ""); let prefix = Path::new("foo/bar/");
1220 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar");
1221 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar");
1222 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "");
1223 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "");
1224 }
1225
1226 #[test]
1227 fn test_prefix_list_dedup() {
1228 let list = PathPrefixes::new(["demo", "demo"]);
1230 assert_eq!(list.len(), 1);
1231 assert_eq!(list[0], Path::new("demo"));
1232 }
1233
1234 #[test]
1235 fn test_prefix_list_overlap() {
1236 let list = PathPrefixes::new(["demo", "demo/foo", "anon"]);
1238 assert_eq!(list.len(), 2);
1239 assert!(list.iter().any(|p| p == &Path::new("demo")));
1240 assert!(list.iter().any(|p| p == &Path::new("anon")));
1241 }
1242
1243 #[test]
1244 fn test_prefix_list_overlap_reverse_order() {
1245 let list = PathPrefixes::new(["demo/foo", "demo"]);
1247 assert_eq!(list.len(), 1);
1248 assert_eq!(list[0], Path::new("demo"));
1249 }
1250
1251 #[test]
1252 fn test_prefix_list_empty_covers_all() {
1253 let list = PathPrefixes::new(["", "demo", "anon"]);
1255 assert_eq!(list.len(), 1);
1256 assert_eq!(list[0], Path::new(""));
1257 }
1258
1259 #[test]
1260 fn test_prefix_list_no_overlap() {
1261 let list = PathPrefixes::new(["demo", "anon", "secret"]);
1263 assert_eq!(list.len(), 3);
1264 }
1265
1266 #[test]
1267 fn test_prefix_list_single() {
1268 let list = PathPrefixes::new(["demo"]);
1269 assert_eq!(list.len(), 1);
1270 }
1271
1272 #[test]
1273 fn test_prefix_list_empty() {
1274 let list = PathPrefixes::new(std::iter::empty::<&str>());
1275 assert!(list.is_empty());
1276 assert_eq!(list.len(), 0);
1277 }
1278
1279 #[test]
1280 fn test_prefix_list_deep_overlap() {
1281 let list = PathPrefixes::new(["a/b/c", "a/b", "a"]);
1283 assert_eq!(list.len(), 1);
1284 assert_eq!(list[0], Path::new("a"));
1285 }
1286
1287 #[test]
1288 fn test_prefix_list_partial_name_not_overlap() {
1289 let list = PathPrefixes::new(["demo", "demonstration"]);
1291 assert_eq!(list.len(), 2);
1292 }
1293
1294 #[test]
1295 fn test_prefix_list_collect() {
1296 let paths: Vec<PathOwned> = vec!["demo".into(), "demo/foo".into()];
1297 let list: PathPrefixes = paths.into_iter().collect();
1298 assert_eq!(list.len(), 1);
1299 assert_eq!(list[0], Path::new("demo"));
1300 }
1301
1302 #[test]
1303 fn test_prefix_list_eq_vec() {
1304 let list = PathPrefixes::new(["demo", "anon"]);
1305 assert_eq!(list, vec!["anon".as_path(), "demo".as_path()]);
1307 }
1308
1309 #[test]
1312 fn test_owned_paths_share_allocation() {
1313 let path = Path::new("customer/room/broadcast").to_owned();
1314
1315 let cloned = path.clone();
1317 assert_eq!(path.as_str().as_ptr(), cloned.as_str().as_ptr());
1318
1319 let requeued = path.as_path().to_owned();
1321 assert_eq!(path.as_str().as_ptr(), requeued.as_str().as_ptr());
1322
1323 let stripped = path.strip_prefix("customer").unwrap().to_owned();
1325 assert_eq!(stripped.as_str(), "room/broadcast");
1326 assert_eq!(stripped.as_str().as_ptr(), path.as_str()["customer/".len()..].as_ptr());
1327
1328 let (dir, rest) = path.next_part().unwrap();
1330 assert_eq!(dir, "customer");
1331 let rest = rest.to_owned();
1332 assert_eq!(rest.as_str().as_ptr(), stripped.as_str().as_ptr());
1333
1334 let joined = path.join("alice");
1336 let joined2 = joined.clone();
1337 assert_eq!(joined.as_str(), "customer/room/broadcast/alice");
1338 assert_eq!(joined.as_str().as_ptr(), joined2.as_str().as_ptr());
1339 }
1340
1341 #[test]
1342 fn test_parts() {
1343 assert_eq!(Path::empty().parts().count(), 0);
1344 assert_eq!(Path::new("foo").parts().collect::<Vec<_>>(), ["foo"]);
1345 assert_eq!(Path::new("/foo//bar/").parts().collect::<Vec<_>>(), ["foo", "bar"]);
1346 }
1347
1348 #[test]
1349 fn test_wire_max_parts() {
1350 use crate::lite::Version;
1351
1352 let ok = (0..Path::MAX_PARTS)
1353 .map(|i| i.to_string())
1354 .collect::<Vec<_>>()
1355 .join("/");
1356 let too_deep = format!("{ok}/extra");
1357
1358 let mut buf = bytes::BytesMut::new();
1360 Path::new(&ok).encode(&mut buf, Version::Lite04).unwrap();
1361 assert!(matches!(
1362 Path::new(&too_deep).encode(&mut bytes::BytesMut::new(), Version::Lite04),
1363 Err(EncodeError::BoundsExceeded)
1364 ));
1365
1366 let decoded = Path::decode(&mut buf.freeze(), Version::Lite04).unwrap();
1368 assert_eq!(decoded.as_str(), ok);
1369
1370 let mut buf = bytes::BytesMut::new();
1372 too_deep.as_str().encode(&mut buf, Version::Lite04).unwrap();
1373 assert!(matches!(
1374 Path::decode(&mut buf.freeze(), Version::Lite04),
1375 Err(DecodeError::BoundsExceeded)
1376 ));
1377 }
1378
1379 #[test]
1380 fn test_owned_empty_paths() {
1381 let empty = Path::new("").to_owned();
1383 assert!(empty.is_empty());
1384 assert_eq!(empty, Path::empty());
1385
1386 let path = Path::new("foo").to_owned();
1387 let rest = path.strip_prefix("foo").unwrap().to_owned();
1388 assert!(rest.is_empty());
1389 }
1390
1391 #[test]
1392 fn test_prefix_list_canonical_order() {
1393 let a = PathPrefixes::new(["foo", "bar"]);
1395 let b = PathPrefixes::new(["bar", "foo"]);
1396 assert_eq!(a, b);
1397 }
1398
1399 #[test]
1400 fn test_path_relative_normalize() {
1401 assert_eq!(PathRelative::new("foo").as_str(), "foo");
1402 assert_eq!(PathRelative::new("/foo/").as_str(), "foo");
1403 assert_eq!(PathRelative::new("foo//bar").as_str(), "foo/bar");
1404 assert_eq!(PathRelative::new("../foo").as_str(), "../foo");
1405 assert_eq!(PathRelative::new("../../a/b").as_str(), "../../a/b");
1406 assert!(PathRelative::new("").is_empty());
1407 }
1408
1409 #[test]
1410 fn test_path_relative_normalizes_dot_segments() {
1411 assert_eq!(PathRelative::new(".").as_str(), ".");
1412 assert_eq!(PathRelative::new("././").as_str(), ".");
1413 assert_eq!(PathRelative::new("./foo").as_str(), "foo");
1414 assert_eq!(PathRelative::new("foo/./bar").as_str(), "foo/bar");
1415 assert_eq!(PathRelative::new("./../foo").as_str(), "../foo");
1416 assert_eq!(PathRelative::from("./foo".to_string()).as_str(), "foo");
1418 assert_eq!(PathRelative::from(".".to_string()).as_str(), ".");
1419 }
1420
1421 #[test]
1422 fn test_resolve_replaces_base_name() {
1423 let base = Path::new("a/b");
1424 assert_eq!(base.resolve(&PathRelative::new("c")).as_str(), "a/c");
1425 assert_eq!(base.resolve(&PathRelative::new("c/d")).as_str(), "a/c/d");
1426 assert_eq!(
1427 Path::new("foo.hang/catalog.pro")
1428 .resolve(&PathRelative::new("./transcode.pro"))
1429 .as_str(),
1430 "foo.hang/transcode.pro"
1431 );
1432 }
1433
1434 #[test]
1435 fn test_resolve_empty_rel_returns_base() {
1436 let base = Path::new("a/b");
1437 assert_eq!(base.resolve(&PathRelative::new("")).as_str(), "a/b");
1438 }
1439
1440 #[test]
1441 fn test_resolve_single_dotdot() {
1442 let base = Path::new("a/b/c");
1443 assert_eq!(base.resolve(&PathRelative::new("../d")).as_str(), "a/d");
1444 assert_eq!(base.resolve(&PathRelative::new("..")).as_str(), "a");
1445 }
1446
1447 #[test]
1448 fn test_resolve_multiple_dotdot() {
1449 let base = Path::new("a/b/c");
1450 assert_eq!(base.resolve(&PathRelative::new("../../x")).as_str(), "x");
1451 assert_eq!(base.resolve(&PathRelative::new("../../../x")).as_str(), "x");
1452 }
1453
1454 #[test]
1455 fn test_resolve_dotdot_clamps_at_root() {
1456 let base = Path::new("a");
1457 assert_eq!(base.resolve(&PathRelative::new("../../../foo")).as_str(), "foo");
1459 assert_eq!(base.resolve(&PathRelative::new("..")).as_str(), "");
1460 }
1461
1462 #[test]
1463 fn test_resolve_empty_base() {
1464 let base = Path::empty();
1465 assert_eq!(base.resolve(&PathRelative::new("foo")).as_str(), "foo");
1466 assert_eq!(base.resolve(&PathRelative::new("..")).as_str(), "");
1467 }
1468
1469 #[test]
1470 fn test_resolve_dot_names_parent() {
1471 let base = Path::new("a/b");
1472 assert_eq!(base.resolve(&PathRelative::new(".")).as_str(), "a");
1473 assert_eq!(base.resolve(&PathRelative::new("./c")).as_str(), "a/c");
1474 assert_eq!(base.resolve(&PathRelative::new("./../c")).as_str(), "c");
1475 }
1476
1477 #[test]
1478 fn test_resolve_self_reference_via_sibling_name() {
1479 let base = Path::new("a/b");
1482 assert_eq!(base.resolve(&PathRelative::new("./b")).as_str(), "a/b");
1483 }
1484
1485 #[test]
1486 fn test_try_resolve_distinguishes_root_from_escape() {
1487 let base = Path::new("top");
1488 assert_eq!(base.try_resolve(&PathRelative::new(".")).unwrap().as_str(), "");
1489 assert!(base.try_resolve(&PathRelative::new("..")).is_none());
1490
1491 let nested = Path::new("a/b");
1492 assert_eq!(nested.try_resolve(&PathRelative::new("..")).unwrap().as_str(), "");
1493 assert!(nested.try_resolve(&PathRelative::new("../..")).is_none());
1494 }
1495}