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 {
343 if rel.is_empty() {
344 return self.to_owned();
345 }
346
347 let mut segments: Vec<&str> = self.parts().collect();
348
349 for seg in rel.as_str().split('/') {
350 if seg == ".." {
351 segments.pop();
352 } else {
353 segments.push(seg);
354 }
355 }
356
357 let path = segments.join("/");
358 if path.is_empty() {
359 Path::empty()
360 } else {
361 Path(Repr::Shared {
362 buf: path.into(),
363 start: 0,
364 })
365 }
366 }
367}
368
369impl<'b> PartialEq<Path<'b>> for Path<'_> {
372 fn eq(&self, other: &Path<'b>) -> bool {
373 self.as_str() == other.as_str()
374 }
375}
376
377impl Eq for Path<'_> {}
378
379impl PartialOrd for Path<'_> {
380 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
381 Some(self.cmp(other))
382 }
383}
384
385impl Ord for Path<'_> {
386 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
387 self.as_str().cmp(other.as_str())
388 }
389}
390
391impl std::hash::Hash for Path<'_> {
392 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
393 self.as_str().hash(state)
394 }
395}
396
397impl fmt::Debug for Path<'_> {
398 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
399 f.debug_tuple("Path").field(&self.as_str()).finish()
400 }
401}
402
403impl serde::Serialize for Path<'_> {
404 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
405 serializer.serialize_str(self.as_str())
406 }
407}
408
409impl<'a> From<&'a str> for Path<'a> {
410 fn from(s: &'a str) -> Self {
411 Self::new(s)
412 }
413}
414
415impl<'a> From<&'a String> for Path<'a> {
416 fn from(s: &'a String) -> Self {
417 Self::new(s)
419 }
420}
421
422impl Default for Path<'_> {
423 fn default() -> Self {
424 Path::empty()
425 }
426}
427
428impl From<String> for Path<'_> {
429 fn from(s: String) -> Self {
430 Path::new(&s).into_owned()
431 }
432}
433
434impl AsRef<str> for Path<'_> {
435 fn as_ref(&self) -> &str {
436 self.as_str()
437 }
438}
439
440impl Display for Path<'_> {
441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442 write!(f, "{}", self.as_str())
443 }
444}
445
446impl<V: Copy> Decode<V> for Path<'_>
447where
448 String: Decode<V>,
449{
450 fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
451 let path: Path = String::decode(r, version)?.into();
452 if path.parts().count() > Path::MAX_PARTS {
453 return Err(DecodeError::BoundsExceeded);
454 }
455 Ok(path)
456 }
457}
458
459impl<V: Copy> Encode<V> for Path<'_>
460where
461 for<'a> &'a str: Encode<V>,
462{
463 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
464 if self.parts().count() > Path::MAX_PARTS {
465 return Err(EncodeError::BoundsExceeded);
466 }
467 self.as_str().encode(w, version)?;
468 Ok(())
469 }
470}
471
472pub type PathRelativeOwned = PathRelative<'static>;
474
475#[derive(Debug, PartialEq, Eq, Hash, Clone, serde::Serialize)]
501pub struct PathRelative<'a>(Cow<'a, str>);
502
503impl<'a> PathRelative<'a> {
504 pub fn new(s: &'a str) -> Self {
509 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
510
511 if needs_normalize_relative(trimmed) {
512 Self(Cow::Owned(normalize_relative_segments(trimmed)))
513 } else {
514 Self(Cow::Borrowed(trimmed))
515 }
516 }
517
518 pub fn as_str(&self) -> &str {
520 &self.0
521 }
522
523 pub fn empty() -> PathRelative<'static> {
525 PathRelative(Cow::Borrowed(""))
526 }
527
528 pub fn is_empty(&self) -> bool {
530 self.0.is_empty()
531 }
532
533 pub fn len(&self) -> usize {
535 self.0.len()
536 }
537
538 pub fn to_owned(&self) -> PathRelativeOwned {
540 PathRelative(Cow::Owned(self.0.to_string()))
541 }
542
543 pub fn into_owned(self) -> PathRelativeOwned {
545 PathRelative(Cow::Owned(self.0.into_owned()))
546 }
547
548 pub fn borrow(&'a self) -> PathRelative<'a> {
550 PathRelative(Cow::Borrowed(&self.0))
551 }
552}
553
554impl<'a> From<&'a str> for PathRelative<'a> {
555 fn from(s: &'a str) -> Self {
556 Self::new(s)
557 }
558}
559
560impl<'a> From<&'a String> for PathRelative<'a> {
561 fn from(s: &'a String) -> Self {
562 Self::new(s)
563 }
564}
565
566impl From<String> for PathRelative<'_> {
567 fn from(s: String) -> Self {
568 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
569
570 if needs_normalize_relative(trimmed) {
571 Self(Cow::Owned(normalize_relative_segments(trimmed)))
572 } else if trimmed == s {
573 Self(Cow::Owned(s))
574 } else {
575 Self(Cow::Owned(trimmed.to_string()))
576 }
577 }
578}
579
580fn needs_normalize_relative(trimmed: &str) -> bool {
581 trimmed.split('/').any(|seg| seg.is_empty() || seg == ".")
582}
583
584fn normalize_relative_segments(trimmed: &str) -> String {
585 trimmed
586 .split('/')
587 .filter(|seg| !seg.is_empty() && *seg != ".")
588 .collect::<Vec<_>>()
589 .join("/")
590}
591
592impl Default for PathRelative<'_> {
593 fn default() -> Self {
594 Self(Cow::Borrowed(""))
595 }
596}
597
598impl AsRef<str> for PathRelative<'_> {
599 fn as_ref(&self) -> &str {
600 &self.0
601 }
602}
603
604impl Display for PathRelative<'_> {
605 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
606 write!(f, "{}", self.0)
607 }
608}
609
610impl<'de> serde::Deserialize<'de> for PathRelative<'static> {
614 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
615 where
616 D: serde::Deserializer<'de>,
617 {
618 let s = String::deserialize(deserializer)?;
619 Ok(PathRelative::from(s))
620 }
621}
622
623#[derive(Debug, Clone, Default, Eq)]
629pub struct PathPrefixes {
630 paths: Vec<PathOwned>,
631}
632
633impl PathPrefixes {
634 pub fn new(paths: impl IntoIterator<Item = impl AsPath>) -> Self {
646 let mut paths: Vec<PathOwned> = paths.into_iter().map(|p| p.as_path().to_owned()).collect();
647
648 if paths.len() <= 1 {
649 return Self { paths };
650 }
651
652 paths.sort_by(|a, b| a.len().cmp(&b.len()).then_with(|| a.as_str().cmp(b.as_str())));
655 paths.dedup();
656
657 let mut result: Vec<PathOwned> = Vec::new();
658 'outer: for path in paths {
659 for existing in &result {
660 if path.has_prefix(existing) {
661 continue 'outer;
662 }
663 }
664 result.push(path);
665 }
666
667 Self { paths: result }
668 }
669
670 pub fn is_empty(&self) -> bool {
672 self.paths.is_empty()
673 }
674
675 pub fn len(&self) -> usize {
677 self.paths.len()
678 }
679
680 pub fn iter(&self) -> std::slice::Iter<'_, PathOwned> {
682 self.paths.iter()
683 }
684}
685
686impl std::ops::Deref for PathPrefixes {
687 type Target = [PathOwned];
688
689 fn deref(&self) -> &[PathOwned] {
690 &self.paths
691 }
692}
693
694impl FromIterator<PathOwned> for PathPrefixes {
695 fn from_iter<I: IntoIterator<Item = PathOwned>>(iter: I) -> Self {
696 Self::new(iter)
697 }
698}
699
700impl From<Vec<PathOwned>> for PathPrefixes {
701 fn from(paths: Vec<PathOwned>) -> Self {
702 Self::new(paths)
703 }
704}
705
706impl<'a> PartialEq<Vec<Path<'a>>> for PathPrefixes {
707 fn eq(&self, other: &Vec<Path<'a>>) -> bool {
708 self.paths == *other
709 }
710}
711
712impl<'a> PartialEq<PathPrefixes> for Vec<Path<'a>> {
713 fn eq(&self, other: &PathPrefixes) -> bool {
714 *self == other.paths
715 }
716}
717
718impl PartialEq for PathPrefixes {
719 fn eq(&self, other: &Self) -> bool {
720 self.paths == other.paths
721 }
722}
723
724impl IntoIterator for PathPrefixes {
725 type Item = PathOwned;
726 type IntoIter = std::vec::IntoIter<PathOwned>;
727
728 fn into_iter(self) -> Self::IntoIter {
729 self.paths.into_iter()
730 }
731}
732
733impl<'a> IntoIterator for &'a PathPrefixes {
734 type Item = &'a PathOwned;
735 type IntoIter = std::slice::Iter<'a, PathOwned>;
736
737 fn into_iter(self) -> Self::IntoIter {
738 self.paths.iter()
739 }
740}
741
742#[cfg(test)]
743mod tests {
744 use super::*;
745
746 #[test]
747 fn test_has_prefix() {
748 let path = Path::new("foo/bar/baz");
749
750 assert!(path.has_prefix(""));
752 assert!(path.has_prefix("foo"));
753 assert!(path.has_prefix(Path::new("foo")));
754 assert!(path.has_prefix("foo/"));
755 assert!(path.has_prefix("foo/bar"));
756 assert!(path.has_prefix(Path::new("foo/bar/")));
757 assert!(path.has_prefix("foo/bar/baz"));
758
759 assert!(!path.has_prefix("f"));
761 assert!(!path.has_prefix(Path::new("fo")));
762 assert!(!path.has_prefix("foo/b"));
763 assert!(!path.has_prefix("foo/ba"));
764 assert!(!path.has_prefix(Path::new("foo/bar/ba")));
765
766 let path = Path::new("foobar");
768 assert!(!path.has_prefix("foo"));
769 assert!(path.has_prefix(Path::new("foobar")));
770 }
771
772 #[test]
773 fn test_strip_prefix() {
774 let path = Path::new("foo/bar/baz");
775
776 assert_eq!(path.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
778 assert_eq!(path.strip_prefix("foo").unwrap().as_str(), "bar/baz");
779 assert_eq!(path.strip_prefix(Path::new("foo/")).unwrap().as_str(), "bar/baz");
780 assert_eq!(path.strip_prefix("foo/bar").unwrap().as_str(), "baz");
781 assert_eq!(path.strip_prefix(Path::new("foo/bar/")).unwrap().as_str(), "baz");
782 assert_eq!(path.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
783
784 assert!(path.strip_prefix("fo").is_none());
786 assert!(path.strip_prefix(Path::new("bar")).is_none());
787 }
788
789 #[test]
790 fn test_join() {
791 assert_eq!(Path::new("foo").join("bar").as_str(), "foo/bar");
793 assert_eq!(Path::new("foo/").join(Path::new("bar")).as_str(), "foo/bar");
794 assert_eq!(Path::new("").join("bar").as_str(), "bar");
795 assert_eq!(Path::new("foo/bar").join(Path::new("baz")).as_str(), "foo/bar/baz");
796 }
797
798 #[test]
799 fn test_empty() {
800 let empty = Path::new("");
801 assert!(empty.is_empty());
802 assert_eq!(empty.len(), 0);
803
804 let non_empty = Path::new("foo");
805 assert!(!non_empty.is_empty());
806 assert_eq!(non_empty.len(), 3);
807 }
808
809 #[test]
810 fn test_from_conversions() {
811 let path1 = Path::from("foo/bar");
812 let path2 = Path::from("foo/bar");
813 let s = String::from("foo/bar");
814 let path3 = Path::from(&s);
815
816 assert_eq!(path1.as_str(), "foo/bar");
817 assert_eq!(path2.as_str(), "foo/bar");
818 assert_eq!(path3.as_str(), "foo/bar");
819 }
820
821 #[test]
822 fn test_path_prefix_join() {
823 let prefix = Path::new("foo");
824 let suffix = Path::new("bar/baz");
825 let path = prefix.join(&suffix);
826 assert_eq!(path.as_str(), "foo/bar/baz");
827
828 let prefix = Path::new("foo/");
829 let suffix = Path::new("bar/baz");
830 let path = prefix.join(&suffix);
831 assert_eq!(path.as_str(), "foo/bar/baz");
832
833 let prefix = Path::new("foo");
834 let suffix = Path::new("/bar/baz");
835 let path = prefix.join(&suffix);
836 assert_eq!(path.as_str(), "foo/bar/baz");
837
838 let prefix = Path::new("");
839 let suffix = Path::new("bar/baz");
840 let path = prefix.join(&suffix);
841 assert_eq!(path.as_str(), "bar/baz");
842 }
843
844 #[test]
845 fn test_path_prefix_conversions() {
846 let prefix1 = Path::from("foo/bar");
847 let prefix2 = Path::from(String::from("foo/bar"));
848 let s = String::from("foo/bar");
849 let prefix3 = Path::from(&s);
850
851 assert_eq!(prefix1.as_str(), "foo/bar");
852 assert_eq!(prefix2.as_str(), "foo/bar");
853 assert_eq!(prefix3.as_str(), "foo/bar");
854 }
855
856 #[test]
857 fn test_path_suffix_conversions() {
858 let suffix1 = Path::from("foo/bar");
859 let suffix2 = Path::from(String::from("foo/bar"));
860 let s = String::from("foo/bar");
861 let suffix3 = Path::from(&s);
862
863 assert_eq!(suffix1.as_str(), "foo/bar");
864 assert_eq!(suffix2.as_str(), "foo/bar");
865 assert_eq!(suffix3.as_str(), "foo/bar");
866 }
867
868 #[test]
869 fn test_path_types_basic_operations() {
870 let prefix = Path::new("foo/bar");
871 assert_eq!(prefix.as_str(), "foo/bar");
872 assert!(!prefix.is_empty());
873 assert_eq!(prefix.len(), 7);
874
875 let suffix = Path::new("baz/qux");
876 assert_eq!(suffix.as_str(), "baz/qux");
877 assert!(!suffix.is_empty());
878 assert_eq!(suffix.len(), 7);
879
880 let empty_prefix = Path::new("");
881 assert!(empty_prefix.is_empty());
882 assert_eq!(empty_prefix.len(), 0);
883
884 let empty_suffix = Path::new("");
885 assert!(empty_suffix.is_empty());
886 assert_eq!(empty_suffix.len(), 0);
887 }
888
889 #[test]
890 fn test_prefix_has_prefix() {
891 let prefix = Path::new("foo/bar");
893 assert!(prefix.has_prefix(""));
894
895 let prefix = Path::new("foo/bar");
897 assert!(prefix.has_prefix("foo/bar"));
898
899 assert!(prefix.has_prefix("foo"));
901 assert!(prefix.has_prefix("foo/"));
902
903 assert!(!prefix.has_prefix("f"));
905 assert!(!prefix.has_prefix("fo"));
906 assert!(!prefix.has_prefix("foo/b"));
907 assert!(!prefix.has_prefix("foo/ba"));
908
909 let prefix = Path::new("foobar");
911 assert!(!prefix.has_prefix("foo"));
912 assert!(prefix.has_prefix("foobar"));
913
914 let prefix = Path::new("foo/bar/");
916 assert!(prefix.has_prefix("foo"));
917 assert!(prefix.has_prefix("foo/"));
918 assert!(prefix.has_prefix("foo/bar"));
919 assert!(prefix.has_prefix("foo/bar/"));
920
921 let prefix = Path::new("foo");
923 assert!(prefix.has_prefix(""));
924 assert!(prefix.has_prefix("foo"));
925 assert!(prefix.has_prefix("foo/")); assert!(!prefix.has_prefix("f"));
927
928 let prefix = Path::new("");
930 assert!(prefix.has_prefix(""));
931 assert!(!prefix.has_prefix("foo"));
932 }
933
934 #[test]
935 fn test_prefix_join() {
936 let prefix = Path::new("foo");
938 let suffix = Path::new("bar");
939 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
940
941 let prefix = Path::new("foo/");
943 let suffix = Path::new("bar");
944 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
945
946 let prefix = Path::new("foo");
948 let suffix = Path::new("/bar");
949 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
950
951 let prefix = Path::new("foo");
953 let suffix = Path::new("bar/");
954 assert_eq!(prefix.join(suffix).as_str(), "foo/bar"); let prefix = Path::new("foo/");
958 let suffix = Path::new("/bar");
959 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
960
961 let prefix = Path::new("foo");
963 let suffix = Path::new("");
964 assert_eq!(prefix.join(suffix).as_str(), "foo");
965
966 let prefix = Path::new("");
968 let suffix = Path::new("bar");
969 assert_eq!(prefix.join(suffix).as_str(), "bar");
970
971 let prefix = Path::new("");
973 let suffix = Path::new("");
974 assert_eq!(prefix.join(suffix).as_str(), "");
975
976 let prefix = Path::new("foo/bar");
978 let suffix = Path::new("baz/qux");
979 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux");
980
981 let prefix = Path::new("foo/bar/");
983 let suffix = Path::new("/baz/qux/");
984 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux"); }
986
987 #[test]
988 fn test_path_ref() {
989 let ref1 = Path::new("/foo/bar/");
991 assert_eq!(ref1.as_str(), "foo/bar");
992
993 let ref2 = Path::from("///foo///");
994 assert_eq!(ref2.as_str(), "foo");
995
996 let ref3 = Path::new("foo//bar///baz");
998 assert_eq!(ref3.as_str(), "foo/bar/baz");
999
1000 let path = Path::new("foo/bar");
1002 let path_ref = path;
1003 assert_eq!(path_ref.as_str(), "foo/bar");
1004
1005 let path2 = Path::new("foo/bar/baz");
1007 assert!(path2.has_prefix(&path_ref));
1008 assert_eq!(path2.strip_prefix(path_ref).unwrap().as_str(), "baz");
1009
1010 let empty = Path::new("");
1012 assert!(empty.is_empty());
1013 assert_eq!(empty.len(), 0);
1014 }
1015
1016 #[test]
1017 fn test_multiple_consecutive_slashes() {
1018 let path = Path::new("foo//bar///baz");
1019 assert_eq!(path.as_str(), "foo/bar/baz");
1021
1022 let path2 = Path::new("//foo//bar///baz//");
1024 assert_eq!(path2.as_str(), "foo/bar/baz");
1025
1026 let path3 = Path::new("foo///bar");
1028 assert_eq!(path3.as_str(), "foo/bar");
1029 }
1030
1031 #[test]
1032 fn test_removes_multiple_slashes_comprehensively() {
1033 assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
1035 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
1036 assert_eq!(Path::new("foo////bar").as_str(), "foo/bar");
1037
1038 assert_eq!(Path::new("foo//bar//baz").as_str(), "foo/bar/baz");
1040 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
1041
1042 assert_eq!(Path::new("foo//bar///baz////qux").as_str(), "foo/bar/baz/qux");
1044
1045 assert_eq!(Path::new("//foo//bar//").as_str(), "foo/bar");
1047 assert_eq!(Path::new("///foo///bar///").as_str(), "foo/bar");
1048
1049 assert_eq!(Path::new("//").as_str(), "");
1051 assert_eq!(Path::new("////").as_str(), "");
1052
1053 let path_with_slashes = Path::new("foo//bar///baz");
1055 assert!(path_with_slashes.has_prefix("foo/bar"));
1056 assert_eq!(path_with_slashes.strip_prefix("foo").unwrap().as_str(), "bar/baz");
1057 assert_eq!(path_with_slashes.join("qux").as_str(), "foo/bar/baz/qux");
1058
1059 let path_ref = Path::new("foo//bar///baz");
1061 assert_eq!(path_ref.as_str(), "foo/bar/baz"); let path_from_ref = path_ref.to_owned();
1063 assert_eq!(path_from_ref.as_str(), "foo/bar/baz"); }
1065
1066 #[test]
1067 fn test_path_ref_multiple_slashes() {
1068 let path_ref = Path::new("//foo//bar///baz//");
1070 assert_eq!(path_ref.as_str(), "foo/bar/baz"); assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
1074 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
1075 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
1076
1077 assert_eq!(Path::new("foo//bar").to_owned().as_str(), "foo/bar");
1079 assert_eq!(Path::new("foo///bar").to_owned().as_str(), "foo/bar");
1080 assert_eq!(Path::new("a//b//c//d").to_owned().as_str(), "a/b/c/d");
1081
1082 assert_eq!(Path::new("//").as_str(), "");
1084 assert_eq!(Path::new("////").as_str(), "");
1085 assert_eq!(Path::new("//").to_owned().as_str(), "");
1086 assert_eq!(Path::new("////").to_owned().as_str(), "");
1087
1088 let normal_path = Path::new("foo/bar/baz");
1090 assert_eq!(normal_path.as_str(), "foo/bar/baz");
1091 let needs_norm = Path::new("foo//bar");
1094 assert_eq!(needs_norm.as_str(), "foo/bar");
1095 }
1097
1098 #[test]
1099 fn test_ergonomic_conversions() {
1100 fn takes_path_ref<'a>(p: impl Into<Path<'a>>) -> String {
1102 p.into().as_str().to_string()
1103 }
1104
1105 fn takes_path_ref_with_trait<'a>(p: impl Into<Path<'a>>) -> String {
1107 p.into().as_str().to_string()
1108 }
1109
1110 assert_eq!(takes_path_ref("foo//bar"), "foo/bar");
1112
1113 let owned_string = String::from("foo//bar///baz");
1115 assert_eq!(takes_path_ref(owned_string), "foo/bar/baz");
1116
1117 let string_ref = String::from("foo//bar");
1119 assert_eq!(takes_path_ref(string_ref), "foo/bar");
1120
1121 let path_ref = Path::new("foo//bar");
1123 assert_eq!(takes_path_ref(path_ref), "foo/bar");
1124
1125 let path = Path::new("foo//bar");
1127 assert_eq!(takes_path_ref(path), "foo/bar");
1128
1129 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");
1137 assert_eq!(takes_path_ref_with_trait(String::from("foo//bar")), "foo/bar");
1138 }
1139
1140 #[test]
1141 fn test_prefix_strip_prefix() {
1142 let prefix = Path::new("foo/bar/baz");
1144 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
1145 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar/baz");
1146 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar/baz");
1147 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "baz");
1148 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "baz");
1149 assert_eq!(prefix.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
1150
1151 assert!(prefix.strip_prefix("fo").is_none());
1153 assert!(prefix.strip_prefix("bar").is_none());
1154 assert!(prefix.strip_prefix("foo/ba").is_none());
1155
1156 let prefix = Path::new("foobar");
1158 assert!(prefix.strip_prefix("foo").is_none());
1159 assert_eq!(prefix.strip_prefix("foobar").unwrap().as_str(), "");
1160
1161 let prefix = Path::new("");
1163 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "");
1164 assert!(prefix.strip_prefix("foo").is_none());
1165
1166 let prefix = Path::new("foo");
1168 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "");
1169 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), ""); let prefix = Path::new("foo/bar/");
1173 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar");
1174 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar");
1175 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "");
1176 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "");
1177 }
1178
1179 #[test]
1180 fn test_prefix_list_dedup() {
1181 let list = PathPrefixes::new(["demo", "demo"]);
1183 assert_eq!(list.len(), 1);
1184 assert_eq!(list[0], Path::new("demo"));
1185 }
1186
1187 #[test]
1188 fn test_prefix_list_overlap() {
1189 let list = PathPrefixes::new(["demo", "demo/foo", "anon"]);
1191 assert_eq!(list.len(), 2);
1192 assert!(list.iter().any(|p| p == &Path::new("demo")));
1193 assert!(list.iter().any(|p| p == &Path::new("anon")));
1194 }
1195
1196 #[test]
1197 fn test_prefix_list_overlap_reverse_order() {
1198 let list = PathPrefixes::new(["demo/foo", "demo"]);
1200 assert_eq!(list.len(), 1);
1201 assert_eq!(list[0], Path::new("demo"));
1202 }
1203
1204 #[test]
1205 fn test_prefix_list_empty_covers_all() {
1206 let list = PathPrefixes::new(["", "demo", "anon"]);
1208 assert_eq!(list.len(), 1);
1209 assert_eq!(list[0], Path::new(""));
1210 }
1211
1212 #[test]
1213 fn test_prefix_list_no_overlap() {
1214 let list = PathPrefixes::new(["demo", "anon", "secret"]);
1216 assert_eq!(list.len(), 3);
1217 }
1218
1219 #[test]
1220 fn test_prefix_list_single() {
1221 let list = PathPrefixes::new(["demo"]);
1222 assert_eq!(list.len(), 1);
1223 }
1224
1225 #[test]
1226 fn test_prefix_list_empty() {
1227 let list = PathPrefixes::new(std::iter::empty::<&str>());
1228 assert!(list.is_empty());
1229 assert_eq!(list.len(), 0);
1230 }
1231
1232 #[test]
1233 fn test_prefix_list_deep_overlap() {
1234 let list = PathPrefixes::new(["a/b/c", "a/b", "a"]);
1236 assert_eq!(list.len(), 1);
1237 assert_eq!(list[0], Path::new("a"));
1238 }
1239
1240 #[test]
1241 fn test_prefix_list_partial_name_not_overlap() {
1242 let list = PathPrefixes::new(["demo", "demonstration"]);
1244 assert_eq!(list.len(), 2);
1245 }
1246
1247 #[test]
1248 fn test_prefix_list_collect() {
1249 let paths: Vec<PathOwned> = vec!["demo".into(), "demo/foo".into()];
1250 let list: PathPrefixes = paths.into_iter().collect();
1251 assert_eq!(list.len(), 1);
1252 assert_eq!(list[0], Path::new("demo"));
1253 }
1254
1255 #[test]
1256 fn test_prefix_list_eq_vec() {
1257 let list = PathPrefixes::new(["demo", "anon"]);
1258 assert_eq!(list, vec!["anon".as_path(), "demo".as_path()]);
1260 }
1261
1262 #[test]
1265 fn test_owned_paths_share_allocation() {
1266 let path = Path::new("customer/room/broadcast").to_owned();
1267
1268 let cloned = path.clone();
1270 assert_eq!(path.as_str().as_ptr(), cloned.as_str().as_ptr());
1271
1272 let requeued = path.as_path().to_owned();
1274 assert_eq!(path.as_str().as_ptr(), requeued.as_str().as_ptr());
1275
1276 let stripped = path.strip_prefix("customer").unwrap().to_owned();
1278 assert_eq!(stripped.as_str(), "room/broadcast");
1279 assert_eq!(stripped.as_str().as_ptr(), path.as_str()["customer/".len()..].as_ptr());
1280
1281 let (dir, rest) = path.next_part().unwrap();
1283 assert_eq!(dir, "customer");
1284 let rest = rest.to_owned();
1285 assert_eq!(rest.as_str().as_ptr(), stripped.as_str().as_ptr());
1286
1287 let joined = path.join("alice");
1289 let joined2 = joined.clone();
1290 assert_eq!(joined.as_str(), "customer/room/broadcast/alice");
1291 assert_eq!(joined.as_str().as_ptr(), joined2.as_str().as_ptr());
1292 }
1293
1294 #[test]
1295 fn test_parts() {
1296 assert_eq!(Path::empty().parts().count(), 0);
1297 assert_eq!(Path::new("foo").parts().collect::<Vec<_>>(), ["foo"]);
1298 assert_eq!(Path::new("/foo//bar/").parts().collect::<Vec<_>>(), ["foo", "bar"]);
1299 }
1300
1301 #[test]
1302 fn test_wire_max_parts() {
1303 use crate::lite::Version;
1304
1305 let ok = (0..Path::MAX_PARTS)
1306 .map(|i| i.to_string())
1307 .collect::<Vec<_>>()
1308 .join("/");
1309 let too_deep = format!("{ok}/extra");
1310
1311 let mut buf = bytes::BytesMut::new();
1313 Path::new(&ok).encode(&mut buf, Version::Lite04).unwrap();
1314 assert!(matches!(
1315 Path::new(&too_deep).encode(&mut bytes::BytesMut::new(), Version::Lite04),
1316 Err(EncodeError::BoundsExceeded)
1317 ));
1318
1319 let decoded = Path::decode(&mut buf.freeze(), Version::Lite04).unwrap();
1321 assert_eq!(decoded.as_str(), ok);
1322
1323 let mut buf = bytes::BytesMut::new();
1325 too_deep.as_str().encode(&mut buf, Version::Lite04).unwrap();
1326 assert!(matches!(
1327 Path::decode(&mut buf.freeze(), Version::Lite04),
1328 Err(DecodeError::BoundsExceeded)
1329 ));
1330 }
1331
1332 #[test]
1333 fn test_owned_empty_paths() {
1334 let empty = Path::new("").to_owned();
1336 assert!(empty.is_empty());
1337 assert_eq!(empty, Path::empty());
1338
1339 let path = Path::new("foo").to_owned();
1340 let rest = path.strip_prefix("foo").unwrap().to_owned();
1341 assert!(rest.is_empty());
1342 }
1343
1344 #[test]
1345 fn test_prefix_list_canonical_order() {
1346 let a = PathPrefixes::new(["foo", "bar"]);
1348 let b = PathPrefixes::new(["bar", "foo"]);
1349 assert_eq!(a, b);
1350 }
1351
1352 #[test]
1353 fn test_path_relative_normalize() {
1354 assert_eq!(PathRelative::new("foo").as_str(), "foo");
1355 assert_eq!(PathRelative::new("/foo/").as_str(), "foo");
1356 assert_eq!(PathRelative::new("foo//bar").as_str(), "foo/bar");
1357 assert_eq!(PathRelative::new("../foo").as_str(), "../foo");
1358 assert_eq!(PathRelative::new("../../a/b").as_str(), "../../a/b");
1359 assert!(PathRelative::new("").is_empty());
1360 }
1361
1362 #[test]
1363 fn test_path_relative_strips_dot_segments() {
1364 assert_eq!(PathRelative::new(".").as_str(), "");
1365 assert_eq!(PathRelative::new("./foo").as_str(), "foo");
1366 assert_eq!(PathRelative::new("foo/./bar").as_str(), "foo/bar");
1367 assert_eq!(PathRelative::new("./../foo").as_str(), "../foo");
1368 assert_eq!(PathRelative::from("./foo".to_string()).as_str(), "foo");
1370 assert_eq!(PathRelative::from(".".to_string()).as_str(), "");
1371 }
1372
1373 #[test]
1374 fn test_resolve_no_dotdot() {
1375 let base = Path::new("a/b");
1376 assert_eq!(base.resolve(&PathRelative::new("c")).as_str(), "a/b/c");
1377 assert_eq!(base.resolve(&PathRelative::new("c/d")).as_str(), "a/b/c/d");
1378 }
1379
1380 #[test]
1381 fn test_resolve_empty_rel_returns_base() {
1382 let base = Path::new("a/b");
1383 assert_eq!(base.resolve(&PathRelative::new("")).as_str(), "a/b");
1384 }
1385
1386 #[test]
1387 fn test_resolve_single_dotdot() {
1388 let base = Path::new("a/b/c");
1389 assert_eq!(base.resolve(&PathRelative::new("../d")).as_str(), "a/b/d");
1390 assert_eq!(base.resolve(&PathRelative::new("..")).as_str(), "a/b");
1391 }
1392
1393 #[test]
1394 fn test_resolve_multiple_dotdot() {
1395 let base = Path::new("a/b/c");
1396 assert_eq!(base.resolve(&PathRelative::new("../../x")).as_str(), "a/x");
1397 assert_eq!(base.resolve(&PathRelative::new("../../../x")).as_str(), "x");
1398 }
1399
1400 #[test]
1401 fn test_resolve_dotdot_clamps_at_root() {
1402 let base = Path::new("a");
1403 assert_eq!(base.resolve(&PathRelative::new("../../../foo")).as_str(), "foo");
1405 assert_eq!(base.resolve(&PathRelative::new("..")).as_str(), "");
1406 }
1407
1408 #[test]
1409 fn test_resolve_empty_base() {
1410 let base = Path::empty();
1411 assert_eq!(base.resolve(&PathRelative::new("foo")).as_str(), "foo");
1412 assert_eq!(base.resolve(&PathRelative::new("..")).as_str(), "");
1413 }
1414
1415 #[test]
1416 fn test_resolve_dot_is_noop() {
1417 let base = Path::new("a/b");
1418 assert_eq!(base.resolve(&PathRelative::new(".")).as_str(), "a/b");
1420 assert_eq!(base.resolve(&PathRelative::new("./c")).as_str(), "a/b/c");
1421 assert_eq!(base.resolve(&PathRelative::new("./../c")).as_str(), "a/c");
1422 }
1423
1424 #[test]
1425 fn test_resolve_self_reference_via_dotdot() {
1426 let base = Path::new("a/b");
1429 assert_eq!(base.resolve(&PathRelative::new("../b")).as_str(), "a/b");
1430 }
1431}