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 pub(crate) fn from_escaped(s: String) -> PathOwned {
128 if s.is_empty() {
129 Path::empty()
130 } else {
131 Path(Repr::Shared {
132 buf: s.into(),
133 start: 0,
134 })
135 }
136 }
137
138 fn slice_from(&'a self, n: usize) -> Path<'a> {
140 match &self.0 {
141 Repr::Borrowed(s) => Path(Repr::Borrowed(&s[n..])),
142 Repr::Shared { buf, start } => Path(Repr::Shared {
143 buf: buf.clone(),
144 start: start + n,
145 }),
146 }
147 }
148
149 pub fn has_prefix(&self, prefix: impl AsPath) -> bool {
171 let prefix = prefix.as_path();
172
173 if prefix.is_empty() {
174 return true;
175 }
176
177 let s = self.as_str();
178 if !s.starts_with(prefix.as_str()) {
179 return false;
180 }
181
182 if s.len() == prefix.len() {
184 return true;
185 }
186
187 s.as_bytes().get(prefix.len()) == Some(&b'/')
189 }
190
191 pub fn strip_prefix(&'a self, prefix: impl AsPath) -> Option<Path<'a>> {
196 let prefix = prefix.as_path();
197
198 if prefix.is_empty() {
199 return Some(self.borrow());
200 }
201
202 let s = self.as_str();
203 if !s.starts_with(prefix.as_str()) {
204 return None;
205 }
206
207 if s.len() == prefix.len() {
209 return Some(Path::empty());
210 }
211
212 if s.as_bytes().get(prefix.len()) != Some(&b'/') {
214 return None;
215 }
216
217 Some(self.slice_from(prefix.len() + 1))
218 }
219
220 pub fn parts(&self) -> impl Iterator<Item = &str> {
233 self.as_str().split('/').filter(|part| !part.is_empty())
236 }
237
238 pub fn next_part(&'a self) -> Option<(&'a str, Path<'a>)> {
240 let s = self.as_str();
241 if s.is_empty() {
242 return None;
243 }
244
245 if let Some(i) = s.find('/') {
246 Some((&s[..i], self.slice_from(i + 1)))
247 } else {
248 Some((s, Path::empty()))
249 }
250 }
251
252 pub fn as_str(&self) -> &str {
254 match &self.0 {
255 Repr::Borrowed(s) => s,
256 Repr::Shared { buf, start } => &buf[*start..],
257 }
258 }
259
260 pub fn empty() -> Path<'static> {
262 Path(Repr::Borrowed(""))
263 }
264
265 pub fn is_empty(&self) -> bool {
267 self.as_str().is_empty()
268 }
269
270 pub fn len(&self) -> usize {
272 self.as_str().len()
273 }
274
275 pub fn to_owned(&self) -> PathOwned {
277 match &self.0 {
278 Repr::Borrowed("") => Path::empty(),
279 Repr::Borrowed(s) => Path(Repr::Shared {
280 buf: Arc::from(*s),
281 start: 0,
282 }),
283 Repr::Shared { buf, start } => Path(Repr::Shared {
284 buf: buf.clone(),
285 start: *start,
286 }),
287 }
288 }
289
290 pub fn into_owned(self) -> PathOwned {
292 match self.0 {
293 Repr::Borrowed("") => Path::empty(),
294 Repr::Borrowed(s) => Path(Repr::Shared {
295 buf: Arc::from(s),
296 start: 0,
297 }),
298 Repr::Shared { buf, start } => Path(Repr::Shared { buf, start }),
299 }
300 }
301
302 pub fn borrow(&'a self) -> Path<'a> {
304 self.slice_from(0)
305 }
306
307 pub fn join(&self, other: impl AsPath) -> PathOwned {
321 let other = other.as_path();
322
323 if self.is_empty() {
324 other.to_owned()
325 } else if other.is_empty() {
326 self.to_owned()
327 } else {
328 Path(Repr::Shared {
330 buf: format!("{}/{}", self.as_str(), other.as_str()).into(),
331 start: 0,
332 })
333 }
334 }
335
336 pub fn resolve(&self, rel: &PathRelative<'_>) -> PathOwned {
356 if rel.is_empty() {
357 return self.to_owned();
358 }
359
360 let mut segments: Vec<&str> = self.parts().collect();
361 segments.pop();
362
363 for seg in rel.as_str().split('/') {
364 if seg == "." {
365 continue;
366 } else if seg == ".." {
367 segments.pop();
368 } else {
369 segments.push(seg);
370 }
371 }
372
373 let path = segments.join("/");
374 if path.is_empty() {
375 Path::empty()
376 } else {
377 Path(Repr::Shared {
378 buf: path.into(),
379 start: 0,
380 })
381 }
382 }
383
384 pub fn try_resolve(&self, rel: &PathRelative<'_>) -> Option<PathOwned> {
390 if rel.is_empty() {
391 return Some(self.to_owned());
392 }
393
394 let mut segments: Vec<&str> = self.parts().collect();
395 segments.pop();
396
397 for seg in rel.as_str().split('/') {
398 if seg == "." {
399 continue;
400 } else if seg == ".." {
401 segments.pop()?;
402 } else {
403 segments.push(seg);
404 }
405 }
406
407 let path = segments.join("/");
408 if path.is_empty() {
409 Some(Path::empty())
410 } else {
411 Some(Path(Repr::Shared {
412 buf: path.into(),
413 start: 0,
414 }))
415 }
416 }
417
418 pub fn relative(&self, base: impl AsPath) -> Option<PathRelativeOwned> {
452 let base = base.as_path();
453
454 if *self == base {
457 return Some(PathRelative::empty());
458 }
459
460 let mut dir: Vec<&str> = base.parts().collect();
462 dir.pop();
463
464 let target: Vec<&str> = self.parts().collect();
465 let common = dir.iter().zip(&target).take_while(|(a, b)| a == b).count();
466
467 let down = &target[common..];
468 if down.iter().any(|part| *part == "." || *part == "..") {
469 return None;
471 }
472
473 let mut rel: Vec<&str> = vec![".."; dir.len() - common];
474 rel.extend(down);
475
476 if rel.is_empty() {
477 return Some(PathRelative::new("."));
479 }
480
481 Some(PathRelativeOwned::from(rel.join("/")))
482 }
483}
484
485impl<'b> PartialEq<Path<'b>> for Path<'_> {
488 fn eq(&self, other: &Path<'b>) -> bool {
489 self.as_str() == other.as_str()
490 }
491}
492
493impl Eq for Path<'_> {}
494
495impl PartialOrd for Path<'_> {
496 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
497 Some(self.cmp(other))
498 }
499}
500
501impl Ord for Path<'_> {
502 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
503 self.as_str().cmp(other.as_str())
504 }
505}
506
507impl std::hash::Hash for Path<'_> {
508 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
509 self.as_str().hash(state)
510 }
511}
512
513impl fmt::Debug for Path<'_> {
514 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
515 f.debug_tuple("Path").field(&self.as_str()).finish()
516 }
517}
518
519impl serde::Serialize for Path<'_> {
520 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
521 serializer.serialize_str(self.as_str())
522 }
523}
524
525impl<'a> From<&'a str> for Path<'a> {
526 fn from(s: &'a str) -> Self {
527 Self::new(s)
528 }
529}
530
531impl<'a> From<&'a String> for Path<'a> {
532 fn from(s: &'a String) -> Self {
533 Self::new(s)
535 }
536}
537
538impl Default for Path<'_> {
539 fn default() -> Self {
540 Path::empty()
541 }
542}
543
544impl From<String> for Path<'_> {
545 fn from(s: String) -> Self {
546 Path::new(&s).into_owned()
547 }
548}
549
550impl AsRef<str> for Path<'_> {
551 fn as_ref(&self) -> &str {
552 self.as_str()
553 }
554}
555
556impl Display for Path<'_> {
557 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
558 write!(f, "{}", self.as_str())
559 }
560}
561
562impl<V: Copy> Decode<V> for Path<'_>
563where
564 String: Decode<V>,
565{
566 fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
567 let path: Path = String::decode(r, version)?.into();
568 if path.parts().count() > Path::MAX_PARTS {
569 return Err(DecodeError::BoundsExceeded);
570 }
571 Ok(path)
572 }
573}
574
575impl<V: Copy> Encode<V> for Path<'_>
576where
577 for<'a> &'a str: Encode<V>,
578{
579 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
580 if self.parts().count() > Path::MAX_PARTS {
581 return Err(EncodeError::BoundsExceeded);
582 }
583 self.as_str().encode(w, version)?;
584 Ok(())
585 }
586}
587
588pub type PathRelativeOwned = PathRelative<'static>;
590
591#[derive(Debug, PartialEq, Eq, Hash, Clone, serde::Serialize)]
619pub struct PathRelative<'a>(Cow<'a, str>);
620
621impl<'a> PathRelative<'a> {
622 pub fn new(s: &'a str) -> Self {
627 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
628
629 if needs_normalize_relative(trimmed) {
630 Self(Cow::Owned(normalize_relative_segments(trimmed)))
631 } else {
632 Self(Cow::Borrowed(trimmed))
633 }
634 }
635
636 pub fn as_str(&self) -> &str {
638 &self.0
639 }
640
641 pub fn empty() -> PathRelative<'static> {
643 PathRelative(Cow::Borrowed(""))
644 }
645
646 pub fn is_empty(&self) -> bool {
648 self.0.is_empty()
649 }
650
651 pub fn len(&self) -> usize {
653 self.0.len()
654 }
655
656 pub fn to_owned(&self) -> PathRelativeOwned {
658 PathRelative(Cow::Owned(self.0.to_string()))
659 }
660
661 pub fn into_owned(self) -> PathRelativeOwned {
663 PathRelative(Cow::Owned(self.0.into_owned()))
664 }
665
666 pub fn borrow(&'a self) -> PathRelative<'a> {
668 PathRelative(Cow::Borrowed(&self.0))
669 }
670}
671
672impl<'a> From<&'a str> for PathRelative<'a> {
673 fn from(s: &'a str) -> Self {
674 Self::new(s)
675 }
676}
677
678impl<'a> From<&'a String> for PathRelative<'a> {
679 fn from(s: &'a String) -> Self {
680 Self::new(s)
681 }
682}
683
684impl From<String> for PathRelative<'_> {
685 fn from(s: String) -> Self {
686 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
687
688 if needs_normalize_relative(trimmed) {
689 Self(Cow::Owned(normalize_relative_segments(trimmed)))
690 } else if trimmed == s {
691 Self(Cow::Owned(s))
692 } else {
693 Self(Cow::Owned(trimmed.to_string()))
694 }
695 }
696}
697
698fn needs_normalize_relative(trimmed: &str) -> bool {
699 trimmed.split('/').any(|seg| seg.is_empty() || seg == ".")
700}
701
702fn normalize_relative_segments(trimmed: &str) -> String {
703 let segments = trimmed
704 .split('/')
705 .filter(|seg| !seg.is_empty() && *seg != ".")
706 .collect::<Vec<_>>()
707 .join("/");
708
709 if segments.is_empty() && trimmed.split('/').any(|seg| seg == ".") {
710 ".".to_string()
711 } else {
712 segments
713 }
714}
715
716impl Default for PathRelative<'_> {
717 fn default() -> Self {
718 Self(Cow::Borrowed(""))
719 }
720}
721
722impl AsRef<str> for PathRelative<'_> {
723 fn as_ref(&self) -> &str {
724 &self.0
725 }
726}
727
728impl Display for PathRelative<'_> {
729 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
730 write!(f, "{}", self.0)
731 }
732}
733
734impl<'de> serde::Deserialize<'de> for PathRelative<'static> {
738 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
739 where
740 D: serde::Deserializer<'de>,
741 {
742 let s = String::deserialize(deserializer)?;
743 Ok(PathRelative::from(s))
744 }
745}
746
747#[derive(Debug, Clone, Default, Eq)]
753pub struct PathPrefixes {
754 paths: Vec<PathOwned>,
755}
756
757impl PathPrefixes {
758 pub fn new(paths: impl IntoIterator<Item = impl AsPath>) -> Self {
770 let mut paths: Vec<PathOwned> = paths.into_iter().map(|p| p.as_path().to_owned()).collect();
771
772 if paths.len() <= 1 {
773 return Self { paths };
774 }
775
776 paths.sort_by(|a, b| a.len().cmp(&b.len()).then_with(|| a.as_str().cmp(b.as_str())));
779 paths.dedup();
780
781 let mut result: Vec<PathOwned> = Vec::new();
782 'outer: for path in paths {
783 for existing in &result {
784 if path.has_prefix(existing) {
785 continue 'outer;
786 }
787 }
788 result.push(path);
789 }
790
791 Self { paths: result }
792 }
793
794 pub fn is_empty(&self) -> bool {
796 self.paths.is_empty()
797 }
798
799 pub fn len(&self) -> usize {
801 self.paths.len()
802 }
803
804 pub fn iter(&self) -> std::slice::Iter<'_, PathOwned> {
806 self.paths.iter()
807 }
808}
809
810impl std::ops::Deref for PathPrefixes {
811 type Target = [PathOwned];
812
813 fn deref(&self) -> &[PathOwned] {
814 &self.paths
815 }
816}
817
818impl FromIterator<PathOwned> for PathPrefixes {
819 fn from_iter<I: IntoIterator<Item = PathOwned>>(iter: I) -> Self {
820 Self::new(iter)
821 }
822}
823
824impl From<Vec<PathOwned>> for PathPrefixes {
825 fn from(paths: Vec<PathOwned>) -> Self {
826 Self::new(paths)
827 }
828}
829
830impl<'a> PartialEq<Vec<Path<'a>>> for PathPrefixes {
831 fn eq(&self, other: &Vec<Path<'a>>) -> bool {
832 self.paths == *other
833 }
834}
835
836impl<'a> PartialEq<PathPrefixes> for Vec<Path<'a>> {
837 fn eq(&self, other: &PathPrefixes) -> bool {
838 *self == other.paths
839 }
840}
841
842impl PartialEq for PathPrefixes {
843 fn eq(&self, other: &Self) -> bool {
844 self.paths == other.paths
845 }
846}
847
848impl IntoIterator for PathPrefixes {
849 type Item = PathOwned;
850 type IntoIter = std::vec::IntoIter<PathOwned>;
851
852 fn into_iter(self) -> Self::IntoIter {
853 self.paths.into_iter()
854 }
855}
856
857impl<'a> IntoIterator for &'a PathPrefixes {
858 type Item = &'a PathOwned;
859 type IntoIter = std::slice::Iter<'a, PathOwned>;
860
861 fn into_iter(self) -> Self::IntoIter {
862 self.paths.iter()
863 }
864}
865
866#[cfg(test)]
867mod tests {
868 use super::*;
869
870 #[test]
871 fn test_has_prefix() {
872 let path = Path::new("foo/bar/baz");
873
874 assert!(path.has_prefix(""));
876 assert!(path.has_prefix("foo"));
877 assert!(path.has_prefix(Path::new("foo")));
878 assert!(path.has_prefix("foo/"));
879 assert!(path.has_prefix("foo/bar"));
880 assert!(path.has_prefix(Path::new("foo/bar/")));
881 assert!(path.has_prefix("foo/bar/baz"));
882
883 assert!(!path.has_prefix("f"));
885 assert!(!path.has_prefix(Path::new("fo")));
886 assert!(!path.has_prefix("foo/b"));
887 assert!(!path.has_prefix("foo/ba"));
888 assert!(!path.has_prefix(Path::new("foo/bar/ba")));
889
890 let path = Path::new("foobar");
892 assert!(!path.has_prefix("foo"));
893 assert!(path.has_prefix(Path::new("foobar")));
894 }
895
896 #[test]
897 fn test_strip_prefix() {
898 let path = Path::new("foo/bar/baz");
899
900 assert_eq!(path.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
902 assert_eq!(path.strip_prefix("foo").unwrap().as_str(), "bar/baz");
903 assert_eq!(path.strip_prefix(Path::new("foo/")).unwrap().as_str(), "bar/baz");
904 assert_eq!(path.strip_prefix("foo/bar").unwrap().as_str(), "baz");
905 assert_eq!(path.strip_prefix(Path::new("foo/bar/")).unwrap().as_str(), "baz");
906 assert_eq!(path.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
907
908 assert!(path.strip_prefix("fo").is_none());
910 assert!(path.strip_prefix(Path::new("bar")).is_none());
911 }
912
913 #[test]
914 fn test_join() {
915 assert_eq!(Path::new("foo").join("bar").as_str(), "foo/bar");
917 assert_eq!(Path::new("foo/").join(Path::new("bar")).as_str(), "foo/bar");
918 assert_eq!(Path::new("").join("bar").as_str(), "bar");
919 assert_eq!(Path::new("foo/bar").join(Path::new("baz")).as_str(), "foo/bar/baz");
920 }
921
922 #[test]
923 fn test_empty() {
924 let empty = Path::new("");
925 assert!(empty.is_empty());
926 assert_eq!(empty.len(), 0);
927
928 let non_empty = Path::new("foo");
929 assert!(!non_empty.is_empty());
930 assert_eq!(non_empty.len(), 3);
931 }
932
933 #[test]
934 fn test_from_conversions() {
935 let path1 = Path::from("foo/bar");
936 let path2 = Path::from("foo/bar");
937 let s = String::from("foo/bar");
938 let path3 = Path::from(&s);
939
940 assert_eq!(path1.as_str(), "foo/bar");
941 assert_eq!(path2.as_str(), "foo/bar");
942 assert_eq!(path3.as_str(), "foo/bar");
943 }
944
945 #[test]
946 fn test_path_prefix_join() {
947 let prefix = Path::new("foo");
948 let suffix = Path::new("bar/baz");
949 let path = prefix.join(&suffix);
950 assert_eq!(path.as_str(), "foo/bar/baz");
951
952 let prefix = Path::new("foo/");
953 let suffix = Path::new("bar/baz");
954 let path = prefix.join(&suffix);
955 assert_eq!(path.as_str(), "foo/bar/baz");
956
957 let prefix = Path::new("foo");
958 let suffix = Path::new("/bar/baz");
959 let path = prefix.join(&suffix);
960 assert_eq!(path.as_str(), "foo/bar/baz");
961
962 let prefix = Path::new("");
963 let suffix = Path::new("bar/baz");
964 let path = prefix.join(&suffix);
965 assert_eq!(path.as_str(), "bar/baz");
966 }
967
968 #[test]
969 fn test_path_prefix_conversions() {
970 let prefix1 = Path::from("foo/bar");
971 let prefix2 = Path::from(String::from("foo/bar"));
972 let s = String::from("foo/bar");
973 let prefix3 = Path::from(&s);
974
975 assert_eq!(prefix1.as_str(), "foo/bar");
976 assert_eq!(prefix2.as_str(), "foo/bar");
977 assert_eq!(prefix3.as_str(), "foo/bar");
978 }
979
980 #[test]
981 fn test_path_suffix_conversions() {
982 let suffix1 = Path::from("foo/bar");
983 let suffix2 = Path::from(String::from("foo/bar"));
984 let s = String::from("foo/bar");
985 let suffix3 = Path::from(&s);
986
987 assert_eq!(suffix1.as_str(), "foo/bar");
988 assert_eq!(suffix2.as_str(), "foo/bar");
989 assert_eq!(suffix3.as_str(), "foo/bar");
990 }
991
992 #[test]
993 fn test_path_types_basic_operations() {
994 let prefix = Path::new("foo/bar");
995 assert_eq!(prefix.as_str(), "foo/bar");
996 assert!(!prefix.is_empty());
997 assert_eq!(prefix.len(), 7);
998
999 let suffix = Path::new("baz/qux");
1000 assert_eq!(suffix.as_str(), "baz/qux");
1001 assert!(!suffix.is_empty());
1002 assert_eq!(suffix.len(), 7);
1003
1004 let empty_prefix = Path::new("");
1005 assert!(empty_prefix.is_empty());
1006 assert_eq!(empty_prefix.len(), 0);
1007
1008 let empty_suffix = Path::new("");
1009 assert!(empty_suffix.is_empty());
1010 assert_eq!(empty_suffix.len(), 0);
1011 }
1012
1013 #[test]
1014 fn test_prefix_has_prefix() {
1015 let prefix = Path::new("foo/bar");
1017 assert!(prefix.has_prefix(""));
1018
1019 let prefix = Path::new("foo/bar");
1021 assert!(prefix.has_prefix("foo/bar"));
1022
1023 assert!(prefix.has_prefix("foo"));
1025 assert!(prefix.has_prefix("foo/"));
1026
1027 assert!(!prefix.has_prefix("f"));
1029 assert!(!prefix.has_prefix("fo"));
1030 assert!(!prefix.has_prefix("foo/b"));
1031 assert!(!prefix.has_prefix("foo/ba"));
1032
1033 let prefix = Path::new("foobar");
1035 assert!(!prefix.has_prefix("foo"));
1036 assert!(prefix.has_prefix("foobar"));
1037
1038 let prefix = Path::new("foo/bar/");
1040 assert!(prefix.has_prefix("foo"));
1041 assert!(prefix.has_prefix("foo/"));
1042 assert!(prefix.has_prefix("foo/bar"));
1043 assert!(prefix.has_prefix("foo/bar/"));
1044
1045 let prefix = Path::new("foo");
1047 assert!(prefix.has_prefix(""));
1048 assert!(prefix.has_prefix("foo"));
1049 assert!(prefix.has_prefix("foo/")); assert!(!prefix.has_prefix("f"));
1051
1052 let prefix = Path::new("");
1054 assert!(prefix.has_prefix(""));
1055 assert!(!prefix.has_prefix("foo"));
1056 }
1057
1058 #[test]
1059 fn test_prefix_join() {
1060 let prefix = Path::new("foo");
1062 let suffix = Path::new("bar");
1063 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
1064
1065 let prefix = Path::new("foo/");
1067 let suffix = Path::new("bar");
1068 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
1069
1070 let prefix = Path::new("foo");
1072 let suffix = Path::new("/bar");
1073 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
1074
1075 let prefix = Path::new("foo");
1077 let suffix = Path::new("bar/");
1078 assert_eq!(prefix.join(suffix).as_str(), "foo/bar"); let prefix = Path::new("foo/");
1082 let suffix = Path::new("/bar");
1083 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
1084
1085 let prefix = Path::new("foo");
1087 let suffix = Path::new("");
1088 assert_eq!(prefix.join(suffix).as_str(), "foo");
1089
1090 let prefix = Path::new("");
1092 let suffix = Path::new("bar");
1093 assert_eq!(prefix.join(suffix).as_str(), "bar");
1094
1095 let prefix = Path::new("");
1097 let suffix = Path::new("");
1098 assert_eq!(prefix.join(suffix).as_str(), "");
1099
1100 let prefix = Path::new("foo/bar");
1102 let suffix = Path::new("baz/qux");
1103 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux");
1104
1105 let prefix = Path::new("foo/bar/");
1107 let suffix = Path::new("/baz/qux/");
1108 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux"); }
1110
1111 #[test]
1112 fn test_path_ref() {
1113 let ref1 = Path::new("/foo/bar/");
1115 assert_eq!(ref1.as_str(), "foo/bar");
1116
1117 let ref2 = Path::from("///foo///");
1118 assert_eq!(ref2.as_str(), "foo");
1119
1120 let ref3 = Path::new("foo//bar///baz");
1122 assert_eq!(ref3.as_str(), "foo/bar/baz");
1123
1124 let path = Path::new("foo/bar");
1126 let path_ref = path;
1127 assert_eq!(path_ref.as_str(), "foo/bar");
1128
1129 let path2 = Path::new("foo/bar/baz");
1131 assert!(path2.has_prefix(&path_ref));
1132 assert_eq!(path2.strip_prefix(path_ref).unwrap().as_str(), "baz");
1133
1134 let empty = Path::new("");
1136 assert!(empty.is_empty());
1137 assert_eq!(empty.len(), 0);
1138 }
1139
1140 #[test]
1141 fn test_multiple_consecutive_slashes() {
1142 let path = Path::new("foo//bar///baz");
1143 assert_eq!(path.as_str(), "foo/bar/baz");
1145
1146 let path2 = Path::new("//foo//bar///baz//");
1148 assert_eq!(path2.as_str(), "foo/bar/baz");
1149
1150 let path3 = Path::new("foo///bar");
1152 assert_eq!(path3.as_str(), "foo/bar");
1153 }
1154
1155 #[test]
1156 fn test_removes_multiple_slashes_comprehensively() {
1157 assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
1159 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
1160 assert_eq!(Path::new("foo////bar").as_str(), "foo/bar");
1161
1162 assert_eq!(Path::new("foo//bar//baz").as_str(), "foo/bar/baz");
1164 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
1165
1166 assert_eq!(Path::new("foo//bar///baz////qux").as_str(), "foo/bar/baz/qux");
1168
1169 assert_eq!(Path::new("//foo//bar//").as_str(), "foo/bar");
1171 assert_eq!(Path::new("///foo///bar///").as_str(), "foo/bar");
1172
1173 assert_eq!(Path::new("//").as_str(), "");
1175 assert_eq!(Path::new("////").as_str(), "");
1176
1177 let path_with_slashes = Path::new("foo//bar///baz");
1179 assert!(path_with_slashes.has_prefix("foo/bar"));
1180 assert_eq!(path_with_slashes.strip_prefix("foo").unwrap().as_str(), "bar/baz");
1181 assert_eq!(path_with_slashes.join("qux").as_str(), "foo/bar/baz/qux");
1182
1183 let path_ref = Path::new("foo//bar///baz");
1185 assert_eq!(path_ref.as_str(), "foo/bar/baz"); let path_from_ref = path_ref.to_owned();
1187 assert_eq!(path_from_ref.as_str(), "foo/bar/baz"); }
1189
1190 #[test]
1191 fn test_path_ref_multiple_slashes() {
1192 let path_ref = Path::new("//foo//bar///baz//");
1194 assert_eq!(path_ref.as_str(), "foo/bar/baz"); assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
1198 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
1199 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
1200
1201 assert_eq!(Path::new("foo//bar").to_owned().as_str(), "foo/bar");
1203 assert_eq!(Path::new("foo///bar").to_owned().as_str(), "foo/bar");
1204 assert_eq!(Path::new("a//b//c//d").to_owned().as_str(), "a/b/c/d");
1205
1206 assert_eq!(Path::new("//").as_str(), "");
1208 assert_eq!(Path::new("////").as_str(), "");
1209 assert_eq!(Path::new("//").to_owned().as_str(), "");
1210 assert_eq!(Path::new("////").to_owned().as_str(), "");
1211
1212 let normal_path = Path::new("foo/bar/baz");
1214 assert_eq!(normal_path.as_str(), "foo/bar/baz");
1215 let needs_norm = Path::new("foo//bar");
1218 assert_eq!(needs_norm.as_str(), "foo/bar");
1219 }
1221
1222 #[test]
1223 fn test_ergonomic_conversions() {
1224 fn takes_path_ref<'a>(p: impl Into<Path<'a>>) -> String {
1226 p.into().as_str().to_string()
1227 }
1228
1229 fn takes_path_ref_with_trait<'a>(p: impl Into<Path<'a>>) -> String {
1231 p.into().as_str().to_string()
1232 }
1233
1234 assert_eq!(takes_path_ref("foo//bar"), "foo/bar");
1236
1237 let owned_string = String::from("foo//bar///baz");
1239 assert_eq!(takes_path_ref(owned_string), "foo/bar/baz");
1240
1241 let string_ref = String::from("foo//bar");
1243 assert_eq!(takes_path_ref(string_ref), "foo/bar");
1244
1245 let path_ref = Path::new("foo//bar");
1247 assert_eq!(takes_path_ref(path_ref), "foo/bar");
1248
1249 let path = Path::new("foo//bar");
1251 assert_eq!(takes_path_ref(path), "foo/bar");
1252
1253 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");
1261 assert_eq!(takes_path_ref_with_trait(String::from("foo//bar")), "foo/bar");
1262 }
1263
1264 #[test]
1265 fn test_prefix_strip_prefix() {
1266 let prefix = Path::new("foo/bar/baz");
1268 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
1269 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar/baz");
1270 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar/baz");
1271 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "baz");
1272 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "baz");
1273 assert_eq!(prefix.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
1274
1275 assert!(prefix.strip_prefix("fo").is_none());
1277 assert!(prefix.strip_prefix("bar").is_none());
1278 assert!(prefix.strip_prefix("foo/ba").is_none());
1279
1280 let prefix = Path::new("foobar");
1282 assert!(prefix.strip_prefix("foo").is_none());
1283 assert_eq!(prefix.strip_prefix("foobar").unwrap().as_str(), "");
1284
1285 let prefix = Path::new("");
1287 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "");
1288 assert!(prefix.strip_prefix("foo").is_none());
1289
1290 let prefix = Path::new("foo");
1292 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "");
1293 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), ""); let prefix = Path::new("foo/bar/");
1297 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar");
1298 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar");
1299 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "");
1300 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "");
1301 }
1302
1303 #[test]
1304 fn test_prefix_list_dedup() {
1305 let list = PathPrefixes::new(["demo", "demo"]);
1307 assert_eq!(list.len(), 1);
1308 assert_eq!(list[0], Path::new("demo"));
1309 }
1310
1311 #[test]
1312 fn test_prefix_list_overlap() {
1313 let list = PathPrefixes::new(["demo", "demo/foo", "anon"]);
1315 assert_eq!(list.len(), 2);
1316 assert!(list.iter().any(|p| p == &Path::new("demo")));
1317 assert!(list.iter().any(|p| p == &Path::new("anon")));
1318 }
1319
1320 #[test]
1321 fn test_prefix_list_overlap_reverse_order() {
1322 let list = PathPrefixes::new(["demo/foo", "demo"]);
1324 assert_eq!(list.len(), 1);
1325 assert_eq!(list[0], Path::new("demo"));
1326 }
1327
1328 #[test]
1329 fn test_prefix_list_empty_covers_all() {
1330 let list = PathPrefixes::new(["", "demo", "anon"]);
1332 assert_eq!(list.len(), 1);
1333 assert_eq!(list[0], Path::new(""));
1334 }
1335
1336 #[test]
1337 fn test_prefix_list_no_overlap() {
1338 let list = PathPrefixes::new(["demo", "anon", "secret"]);
1340 assert_eq!(list.len(), 3);
1341 }
1342
1343 #[test]
1344 fn test_prefix_list_single() {
1345 let list = PathPrefixes::new(["demo"]);
1346 assert_eq!(list.len(), 1);
1347 }
1348
1349 #[test]
1350 fn test_prefix_list_empty() {
1351 let list = PathPrefixes::new(std::iter::empty::<&str>());
1352 assert!(list.is_empty());
1353 assert_eq!(list.len(), 0);
1354 }
1355
1356 #[test]
1357 fn test_prefix_list_deep_overlap() {
1358 let list = PathPrefixes::new(["a/b/c", "a/b", "a"]);
1360 assert_eq!(list.len(), 1);
1361 assert_eq!(list[0], Path::new("a"));
1362 }
1363
1364 #[test]
1365 fn test_prefix_list_partial_name_not_overlap() {
1366 let list = PathPrefixes::new(["demo", "demonstration"]);
1368 assert_eq!(list.len(), 2);
1369 }
1370
1371 #[test]
1372 fn test_prefix_list_collect() {
1373 let paths: Vec<PathOwned> = vec!["demo".into(), "demo/foo".into()];
1374 let list: PathPrefixes = paths.into_iter().collect();
1375 assert_eq!(list.len(), 1);
1376 assert_eq!(list[0], Path::new("demo"));
1377 }
1378
1379 #[test]
1380 fn test_prefix_list_eq_vec() {
1381 let list = PathPrefixes::new(["demo", "anon"]);
1382 assert_eq!(list, vec!["anon".as_path(), "demo".as_path()]);
1384 }
1385
1386 #[test]
1389 fn test_owned_paths_share_allocation() {
1390 let path = Path::new("customer/room/broadcast").to_owned();
1391
1392 let cloned = path.clone();
1394 assert_eq!(path.as_str().as_ptr(), cloned.as_str().as_ptr());
1395
1396 let requeued = path.as_path().to_owned();
1398 assert_eq!(path.as_str().as_ptr(), requeued.as_str().as_ptr());
1399
1400 let stripped = path.strip_prefix("customer").unwrap().to_owned();
1402 assert_eq!(stripped.as_str(), "room/broadcast");
1403 assert_eq!(stripped.as_str().as_ptr(), path.as_str()["customer/".len()..].as_ptr());
1404
1405 let (dir, rest) = path.next_part().unwrap();
1407 assert_eq!(dir, "customer");
1408 let rest = rest.to_owned();
1409 assert_eq!(rest.as_str().as_ptr(), stripped.as_str().as_ptr());
1410
1411 let joined = path.join("alice");
1413 let joined2 = joined.clone();
1414 assert_eq!(joined.as_str(), "customer/room/broadcast/alice");
1415 assert_eq!(joined.as_str().as_ptr(), joined2.as_str().as_ptr());
1416 }
1417
1418 #[test]
1419 fn test_parts() {
1420 assert_eq!(Path::empty().parts().count(), 0);
1421 assert_eq!(Path::new("foo").parts().collect::<Vec<_>>(), ["foo"]);
1422 assert_eq!(Path::new("/foo//bar/").parts().collect::<Vec<_>>(), ["foo", "bar"]);
1423 }
1424
1425 #[test]
1426 fn test_wire_max_parts() {
1427 use crate::lite::Version;
1428
1429 let ok = (0..Path::MAX_PARTS)
1430 .map(|i| i.to_string())
1431 .collect::<Vec<_>>()
1432 .join("/");
1433 let too_deep = format!("{ok}/extra");
1434
1435 let mut buf = bytes::BytesMut::new();
1437 Path::new(&ok).encode(&mut buf, Version::Lite04).unwrap();
1438 assert!(matches!(
1439 Path::new(&too_deep).encode(&mut bytes::BytesMut::new(), Version::Lite04),
1440 Err(EncodeError::BoundsExceeded)
1441 ));
1442
1443 let decoded = Path::decode(&mut buf.freeze(), Version::Lite04).unwrap();
1445 assert_eq!(decoded.as_str(), ok);
1446
1447 let mut buf = bytes::BytesMut::new();
1449 too_deep.as_str().encode(&mut buf, Version::Lite04).unwrap();
1450 assert!(matches!(
1451 Path::decode(&mut buf.freeze(), Version::Lite04),
1452 Err(DecodeError::BoundsExceeded)
1453 ));
1454 }
1455
1456 #[test]
1457 fn test_owned_empty_paths() {
1458 let empty = Path::new("").to_owned();
1460 assert!(empty.is_empty());
1461 assert_eq!(empty, Path::empty());
1462
1463 let path = Path::new("foo").to_owned();
1464 let rest = path.strip_prefix("foo").unwrap().to_owned();
1465 assert!(rest.is_empty());
1466 }
1467
1468 #[test]
1469 fn test_prefix_list_canonical_order() {
1470 let a = PathPrefixes::new(["foo", "bar"]);
1472 let b = PathPrefixes::new(["bar", "foo"]);
1473 assert_eq!(a, b);
1474 }
1475
1476 #[test]
1477 fn test_path_relative_normalize() {
1478 assert_eq!(PathRelative::new("foo").as_str(), "foo");
1479 assert_eq!(PathRelative::new("/foo/").as_str(), "foo");
1480 assert_eq!(PathRelative::new("foo//bar").as_str(), "foo/bar");
1481 assert_eq!(PathRelative::new("../foo").as_str(), "../foo");
1482 assert_eq!(PathRelative::new("../../a/b").as_str(), "../../a/b");
1483 assert!(PathRelative::new("").is_empty());
1484 }
1485
1486 #[test]
1487 fn test_path_relative_normalizes_dot_segments() {
1488 assert_eq!(PathRelative::new(".").as_str(), ".");
1489 assert_eq!(PathRelative::new("././").as_str(), ".");
1490 assert_eq!(PathRelative::new("./foo").as_str(), "foo");
1491 assert_eq!(PathRelative::new("foo/./bar").as_str(), "foo/bar");
1492 assert_eq!(PathRelative::new("./../foo").as_str(), "../foo");
1493 assert_eq!(PathRelative::from("./foo".to_string()).as_str(), "foo");
1495 assert_eq!(PathRelative::from(".".to_string()).as_str(), ".");
1496 }
1497
1498 #[test]
1499 fn test_resolve_replaces_base_name() {
1500 let base = Path::new("a/b");
1501 assert_eq!(base.resolve(&PathRelative::new("c")).as_str(), "a/c");
1502 assert_eq!(base.resolve(&PathRelative::new("c/d")).as_str(), "a/c/d");
1503 assert_eq!(
1504 Path::new("foo.hang/catalog.pro")
1505 .resolve(&PathRelative::new("./transcode.pro"))
1506 .as_str(),
1507 "foo.hang/transcode.pro"
1508 );
1509 }
1510
1511 #[test]
1512 fn test_resolve_empty_rel_returns_base() {
1513 let base = Path::new("a/b");
1514 assert_eq!(base.resolve(&PathRelative::new("")).as_str(), "a/b");
1515 }
1516
1517 #[test]
1518 fn test_resolve_single_dotdot() {
1519 let base = Path::new("a/b/c");
1520 assert_eq!(base.resolve(&PathRelative::new("../d")).as_str(), "a/d");
1521 assert_eq!(base.resolve(&PathRelative::new("..")).as_str(), "a");
1522 }
1523
1524 #[test]
1525 fn test_resolve_multiple_dotdot() {
1526 let base = Path::new("a/b/c");
1527 assert_eq!(base.resolve(&PathRelative::new("../../x")).as_str(), "x");
1528 assert_eq!(base.resolve(&PathRelative::new("../../../x")).as_str(), "x");
1529 }
1530
1531 #[test]
1532 fn test_resolve_dotdot_clamps_at_root() {
1533 let base = Path::new("a");
1534 assert_eq!(base.resolve(&PathRelative::new("../../../foo")).as_str(), "foo");
1536 assert_eq!(base.resolve(&PathRelative::new("..")).as_str(), "");
1537 }
1538
1539 #[test]
1540 fn test_resolve_empty_base() {
1541 let base = Path::empty();
1542 assert_eq!(base.resolve(&PathRelative::new("foo")).as_str(), "foo");
1543 assert_eq!(base.resolve(&PathRelative::new("..")).as_str(), "");
1544 }
1545
1546 #[test]
1547 fn test_resolve_dot_names_parent() {
1548 let base = Path::new("a/b");
1549 assert_eq!(base.resolve(&PathRelative::new(".")).as_str(), "a");
1550 assert_eq!(base.resolve(&PathRelative::new("./c")).as_str(), "a/c");
1551 assert_eq!(base.resolve(&PathRelative::new("./../c")).as_str(), "c");
1552 }
1553
1554 #[test]
1555 fn test_resolve_self_reference_via_sibling_name() {
1556 let base = Path::new("a/b");
1559 assert_eq!(base.resolve(&PathRelative::new("./b")).as_str(), "a/b");
1560 }
1561
1562 #[test]
1563 fn test_try_resolve_distinguishes_root_from_escape() {
1564 let base = Path::new("top");
1565 assert_eq!(base.try_resolve(&PathRelative::new(".")).unwrap().as_str(), "");
1566 assert!(base.try_resolve(&PathRelative::new("..")).is_none());
1567
1568 let nested = Path::new("a/b");
1569 assert_eq!(nested.try_resolve(&PathRelative::new("..")).unwrap().as_str(), "");
1570 assert!(nested.try_resolve(&PathRelative::new("../..")).is_none());
1571 }
1572
1573 #[test]
1574 fn test_relative() {
1575 let rel = |target: &str, base: &str| Path::new(target).relative(base).unwrap();
1576
1577 assert_eq!(rel("foo/bar/baz", "foo/bar").as_str(), "bar/baz");
1579 assert_eq!(rel("foo/baz", "foo/bar").as_str(), "baz");
1581 assert_eq!(rel("foo/baz/bar", "foo/bar/baz").as_str(), "../baz/bar");
1583 assert_eq!(rel("a/b", "a/b/transcode.hang").as_str(), ".");
1585 assert_eq!(rel("a/b", "a/b/one/two/transcode.hang").as_str(), "../..");
1586 assert_eq!(rel("foo/bar", "").as_str(), "foo/bar");
1588 assert_eq!(rel("", "foo").as_str(), ".");
1589 assert_eq!(rel("a/b", "a/b").as_str(), "");
1591 assert_eq!(rel("", "").as_str(), "");
1592 assert_eq!(rel("/a//b/", "//a/b/dir//").as_str(), ".");
1594 }
1595
1596 #[test]
1597 fn test_relative_rejects_unnameable_targets() {
1598 assert!(Path::new("a/../b").relative("").is_none());
1601 assert!(Path::new("x/./y").relative("x/z").is_none());
1602 assert!(Path::new("a/..").relative("a/b").is_none());
1603
1604 assert_eq!(Path::new("a/..").relative("a/..").unwrap().as_str(), "");
1606
1607 let rel = Path::new("a/../b/x").relative("a/../b/c").unwrap();
1609 assert_eq!(rel.as_str(), "x");
1610 assert_eq!(Path::new("a/../b/c").resolve(&rel).as_str(), "a/../b/x");
1611 }
1612
1613 #[test]
1614 fn test_relative_round_trips() {
1615 let paths = [
1616 "", "a", "b", "a/b", "a/c", "a/b/c", "a/b/c/d", "x/y/z", "a/../b", "a/./b", "a/..", "a/.",
1617 ];
1618
1619 for base in paths {
1620 for target in paths {
1621 let base = Path::new(base);
1622 let target = Path::new(target);
1623 let Some(rel) = target.relative(&base) else {
1624 assert!(
1626 target != base && target.parts().any(|part| part == "." || part == ".."),
1627 "{base} -> {target} refused a nameable target"
1628 );
1629 continue;
1630 };
1631
1632 assert_eq!(base.resolve(&rel), target, "{base} -> {target} via {rel}");
1633 assert!(
1635 base.try_resolve(&rel).is_some(),
1636 "{base} -> {target} via {rel} escaped the root"
1637 );
1638 }
1639 }
1640 }
1641}