1use std::fmt::{self, Display};
2use std::sync::Arc;
3
4use crate::coding::{Decode, DecodeError, Encode, EncodeError};
5
6pub type PathOwned = Path<'static>;
8
9pub trait AsPath {
14 fn as_path(&self) -> Path<'_>;
15}
16
17impl<'a> AsPath for &'a str {
18 fn as_path(&self) -> Path<'a> {
19 Path::new(self)
20 }
21}
22
23impl<'a> AsPath for &'a Path<'a> {
24 fn as_path(&self) -> Path<'a> {
25 self.borrow()
27 }
28}
29
30impl AsPath for Path<'_> {
31 fn as_path(&self) -> Path<'_> {
32 self.borrow()
33 }
34}
35
36impl AsPath for String {
37 fn as_path(&self) -> Path<'_> {
38 Path::new(self)
39 }
40}
41
42impl<'a> AsPath for &'a String {
43 fn as_path(&self) -> Path<'a> {
44 Path::new(self)
45 }
46}
47
48#[derive(Clone)]
54enum Repr<'a> {
55 Borrowed(&'a str),
56 Shared { buf: Arc<str>, start: usize },
57}
58
59#[derive(Clone)]
90pub struct Path<'a>(Repr<'a>);
91
92impl<'a> Path<'a> {
93 pub const MAX_PARTS: usize = 32;
99
100 pub fn new(s: &'a str) -> Self {
105 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
106
107 if trimmed.contains("//") {
109 let normalized = trimmed
111 .split('/')
112 .filter(|s| !s.is_empty())
113 .collect::<Vec<_>>()
114 .join("/");
115 Self(Repr::Shared {
116 buf: normalized.into(),
117 start: 0,
118 })
119 } else {
120 Self(Repr::Borrowed(trimmed))
122 }
123 }
124
125 fn slice_from(&'a self, n: usize) -> Path<'a> {
127 match &self.0 {
128 Repr::Borrowed(s) => Path(Repr::Borrowed(&s[n..])),
129 Repr::Shared { buf, start } => Path(Repr::Shared {
130 buf: buf.clone(),
131 start: start + n,
132 }),
133 }
134 }
135
136 pub fn has_prefix(&self, prefix: impl AsPath) -> bool {
158 let prefix = prefix.as_path();
159
160 if prefix.is_empty() {
161 return true;
162 }
163
164 let s = self.as_str();
165 if !s.starts_with(prefix.as_str()) {
166 return false;
167 }
168
169 if s.len() == prefix.len() {
171 return true;
172 }
173
174 s.as_bytes().get(prefix.len()) == Some(&b'/')
176 }
177
178 pub fn strip_prefix(&'a self, prefix: impl AsPath) -> Option<Path<'a>> {
179 let prefix = prefix.as_path();
180
181 if prefix.is_empty() {
182 return Some(self.borrow());
183 }
184
185 let s = self.as_str();
186 if !s.starts_with(prefix.as_str()) {
187 return None;
188 }
189
190 if s.len() == prefix.len() {
192 return Some(Path::empty());
193 }
194
195 if s.as_bytes().get(prefix.len()) != Some(&b'/') {
197 return None;
198 }
199
200 Some(self.slice_from(prefix.len() + 1))
201 }
202
203 pub fn parts(&self) -> impl Iterator<Item = &str> {
216 self.as_str().split('/').filter(|part| !part.is_empty())
219 }
220
221 pub fn next_part(&'a self) -> Option<(&'a str, Path<'a>)> {
223 let s = self.as_str();
224 if s.is_empty() {
225 return None;
226 }
227
228 if let Some(i) = s.find('/') {
229 Some((&s[..i], self.slice_from(i + 1)))
230 } else {
231 Some((s, Path::empty()))
232 }
233 }
234
235 pub fn as_str(&self) -> &str {
236 match &self.0 {
237 Repr::Borrowed(s) => s,
238 Repr::Shared { buf, start } => &buf[*start..],
239 }
240 }
241
242 pub fn empty() -> Path<'static> {
243 Path(Repr::Borrowed(""))
244 }
245
246 pub fn is_empty(&self) -> bool {
247 self.as_str().is_empty()
248 }
249
250 pub fn len(&self) -> usize {
251 self.as_str().len()
252 }
253
254 pub fn to_owned(&self) -> PathOwned {
255 match &self.0 {
256 Repr::Borrowed("") => Path::empty(),
257 Repr::Borrowed(s) => Path(Repr::Shared {
258 buf: Arc::from(*s),
259 start: 0,
260 }),
261 Repr::Shared { buf, start } => Path(Repr::Shared {
262 buf: buf.clone(),
263 start: *start,
264 }),
265 }
266 }
267
268 pub fn into_owned(self) -> PathOwned {
269 match self.0 {
270 Repr::Borrowed("") => Path::empty(),
271 Repr::Borrowed(s) => Path(Repr::Shared {
272 buf: Arc::from(s),
273 start: 0,
274 }),
275 Repr::Shared { buf, start } => Path(Repr::Shared { buf, start }),
276 }
277 }
278
279 pub fn borrow(&'a self) -> Path<'a> {
281 self.slice_from(0)
282 }
283
284 pub fn join(&self, other: impl AsPath) -> PathOwned {
298 let other = other.as_path();
299
300 if self.is_empty() {
301 other.to_owned()
302 } else if other.is_empty() {
303 self.to_owned()
304 } else {
305 Path(Repr::Shared {
307 buf: format!("{}/{}", self.as_str(), other.as_str()).into(),
308 start: 0,
309 })
310 }
311 }
312}
313
314impl<'b> PartialEq<Path<'b>> for Path<'_> {
317 fn eq(&self, other: &Path<'b>) -> bool {
318 self.as_str() == other.as_str()
319 }
320}
321
322impl Eq for Path<'_> {}
323
324impl PartialOrd for Path<'_> {
325 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
326 Some(self.cmp(other))
327 }
328}
329
330impl Ord for Path<'_> {
331 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
332 self.as_str().cmp(other.as_str())
333 }
334}
335
336impl std::hash::Hash for Path<'_> {
337 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
338 self.as_str().hash(state)
339 }
340}
341
342impl fmt::Debug for Path<'_> {
343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 f.debug_tuple("Path").field(&self.as_str()).finish()
345 }
346}
347
348#[cfg(feature = "serde")]
349impl serde::Serialize for Path<'_> {
350 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
351 serializer.serialize_str(self.as_str())
352 }
353}
354
355impl<'a> From<&'a str> for Path<'a> {
356 fn from(s: &'a str) -> Self {
357 Self::new(s)
358 }
359}
360
361impl<'a> From<&'a String> for Path<'a> {
362 fn from(s: &'a String) -> Self {
363 Self::new(s)
365 }
366}
367
368impl Default for Path<'_> {
369 fn default() -> Self {
370 Path::empty()
371 }
372}
373
374impl From<String> for Path<'_> {
375 fn from(s: String) -> Self {
376 Path::new(&s).into_owned()
377 }
378}
379
380impl AsRef<str> for Path<'_> {
381 fn as_ref(&self) -> &str {
382 self.as_str()
383 }
384}
385
386impl Display for Path<'_> {
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 write!(f, "{}", self.as_str())
389 }
390}
391
392impl<V: Copy> Decode<V> for Path<'_>
393where
394 String: Decode<V>,
395{
396 fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
397 let path: Path = String::decode(r, version)?.into();
398 if path.parts().count() > Path::MAX_PARTS {
399 return Err(DecodeError::BoundsExceeded);
400 }
401 Ok(path)
402 }
403}
404
405impl<V: Copy> Encode<V> for Path<'_>
406where
407 for<'a> &'a str: Encode<V>,
408{
409 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
410 if self.parts().count() > Path::MAX_PARTS {
411 return Err(EncodeError::BoundsExceeded);
412 }
413 self.as_str().encode(w, version)?;
414 Ok(())
415 }
416}
417
418#[cfg(feature = "serde")]
420impl<'de: 'a, 'a> serde::Deserialize<'de> for Path<'a> {
421 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
422 where
423 D: serde::Deserializer<'de>,
424 {
425 let s = <&'a str as serde::Deserialize<'de>>::deserialize(deserializer)?;
426 Ok(Path::new(s))
427 }
428}
429
430#[derive(Debug, Clone, Default, Eq)]
436pub struct PathPrefixes {
437 paths: Vec<PathOwned>,
438}
439
440impl PathPrefixes {
441 pub fn new(paths: impl IntoIterator<Item = impl AsPath>) -> Self {
453 let mut paths: Vec<PathOwned> = paths.into_iter().map(|p| p.as_path().to_owned()).collect();
454
455 if paths.len() <= 1 {
456 return Self { paths };
457 }
458
459 paths.sort_by(|a, b| a.len().cmp(&b.len()).then_with(|| a.as_str().cmp(b.as_str())));
462 paths.dedup();
463
464 let mut result: Vec<PathOwned> = Vec::new();
465 'outer: for path in paths {
466 for existing in &result {
467 if path.has_prefix(existing) {
468 continue 'outer;
469 }
470 }
471 result.push(path);
472 }
473
474 Self { paths: result }
475 }
476
477 pub fn is_empty(&self) -> bool {
478 self.paths.is_empty()
479 }
480
481 pub fn len(&self) -> usize {
482 self.paths.len()
483 }
484
485 pub fn iter(&self) -> std::slice::Iter<'_, PathOwned> {
486 self.paths.iter()
487 }
488}
489
490impl std::ops::Deref for PathPrefixes {
491 type Target = [PathOwned];
492
493 fn deref(&self) -> &[PathOwned] {
494 &self.paths
495 }
496}
497
498impl FromIterator<PathOwned> for PathPrefixes {
499 fn from_iter<I: IntoIterator<Item = PathOwned>>(iter: I) -> Self {
500 Self::new(iter)
501 }
502}
503
504impl From<Vec<PathOwned>> for PathPrefixes {
505 fn from(paths: Vec<PathOwned>) -> Self {
506 Self::new(paths)
507 }
508}
509
510impl<'a> PartialEq<Vec<Path<'a>>> for PathPrefixes {
511 fn eq(&self, other: &Vec<Path<'a>>) -> bool {
512 self.paths == *other
513 }
514}
515
516impl<'a> PartialEq<PathPrefixes> for Vec<Path<'a>> {
517 fn eq(&self, other: &PathPrefixes) -> bool {
518 *self == other.paths
519 }
520}
521
522impl PartialEq for PathPrefixes {
523 fn eq(&self, other: &Self) -> bool {
524 self.paths == other.paths
525 }
526}
527
528impl IntoIterator for PathPrefixes {
529 type Item = PathOwned;
530 type IntoIter = std::vec::IntoIter<PathOwned>;
531
532 fn into_iter(self) -> Self::IntoIter {
533 self.paths.into_iter()
534 }
535}
536
537impl<'a> IntoIterator for &'a PathPrefixes {
538 type Item = &'a PathOwned;
539 type IntoIter = std::slice::Iter<'a, PathOwned>;
540
541 fn into_iter(self) -> Self::IntoIter {
542 self.paths.iter()
543 }
544}
545
546#[cfg(test)]
547mod tests {
548 use super::*;
549
550 #[test]
551 fn test_has_prefix() {
552 let path = Path::new("foo/bar/baz");
553
554 assert!(path.has_prefix(""));
556 assert!(path.has_prefix("foo"));
557 assert!(path.has_prefix(Path::new("foo")));
558 assert!(path.has_prefix("foo/"));
559 assert!(path.has_prefix("foo/bar"));
560 assert!(path.has_prefix(Path::new("foo/bar/")));
561 assert!(path.has_prefix("foo/bar/baz"));
562
563 assert!(!path.has_prefix("f"));
565 assert!(!path.has_prefix(Path::new("fo")));
566 assert!(!path.has_prefix("foo/b"));
567 assert!(!path.has_prefix("foo/ba"));
568 assert!(!path.has_prefix(Path::new("foo/bar/ba")));
569
570 let path = Path::new("foobar");
572 assert!(!path.has_prefix("foo"));
573 assert!(path.has_prefix(Path::new("foobar")));
574 }
575
576 #[test]
577 fn test_strip_prefix() {
578 let path = Path::new("foo/bar/baz");
579
580 assert_eq!(path.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
582 assert_eq!(path.strip_prefix("foo").unwrap().as_str(), "bar/baz");
583 assert_eq!(path.strip_prefix(Path::new("foo/")).unwrap().as_str(), "bar/baz");
584 assert_eq!(path.strip_prefix("foo/bar").unwrap().as_str(), "baz");
585 assert_eq!(path.strip_prefix(Path::new("foo/bar/")).unwrap().as_str(), "baz");
586 assert_eq!(path.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
587
588 assert!(path.strip_prefix("fo").is_none());
590 assert!(path.strip_prefix(Path::new("bar")).is_none());
591 }
592
593 #[test]
594 fn test_join() {
595 assert_eq!(Path::new("foo").join("bar").as_str(), "foo/bar");
597 assert_eq!(Path::new("foo/").join(Path::new("bar")).as_str(), "foo/bar");
598 assert_eq!(Path::new("").join("bar").as_str(), "bar");
599 assert_eq!(Path::new("foo/bar").join(Path::new("baz")).as_str(), "foo/bar/baz");
600 }
601
602 #[test]
603 fn test_empty() {
604 let empty = Path::new("");
605 assert!(empty.is_empty());
606 assert_eq!(empty.len(), 0);
607
608 let non_empty = Path::new("foo");
609 assert!(!non_empty.is_empty());
610 assert_eq!(non_empty.len(), 3);
611 }
612
613 #[test]
614 fn test_from_conversions() {
615 let path1 = Path::from("foo/bar");
616 let path2 = Path::from("foo/bar");
617 let s = String::from("foo/bar");
618 let path3 = Path::from(&s);
619
620 assert_eq!(path1.as_str(), "foo/bar");
621 assert_eq!(path2.as_str(), "foo/bar");
622 assert_eq!(path3.as_str(), "foo/bar");
623 }
624
625 #[test]
626 fn test_path_prefix_join() {
627 let prefix = Path::new("foo");
628 let suffix = Path::new("bar/baz");
629 let path = prefix.join(&suffix);
630 assert_eq!(path.as_str(), "foo/bar/baz");
631
632 let prefix = Path::new("foo/");
633 let suffix = Path::new("bar/baz");
634 let path = prefix.join(&suffix);
635 assert_eq!(path.as_str(), "foo/bar/baz");
636
637 let prefix = Path::new("foo");
638 let suffix = Path::new("/bar/baz");
639 let path = prefix.join(&suffix);
640 assert_eq!(path.as_str(), "foo/bar/baz");
641
642 let prefix = Path::new("");
643 let suffix = Path::new("bar/baz");
644 let path = prefix.join(&suffix);
645 assert_eq!(path.as_str(), "bar/baz");
646 }
647
648 #[test]
649 fn test_path_prefix_conversions() {
650 let prefix1 = Path::from("foo/bar");
651 let prefix2 = Path::from(String::from("foo/bar"));
652 let s = String::from("foo/bar");
653 let prefix3 = Path::from(&s);
654
655 assert_eq!(prefix1.as_str(), "foo/bar");
656 assert_eq!(prefix2.as_str(), "foo/bar");
657 assert_eq!(prefix3.as_str(), "foo/bar");
658 }
659
660 #[test]
661 fn test_path_suffix_conversions() {
662 let suffix1 = Path::from("foo/bar");
663 let suffix2 = Path::from(String::from("foo/bar"));
664 let s = String::from("foo/bar");
665 let suffix3 = Path::from(&s);
666
667 assert_eq!(suffix1.as_str(), "foo/bar");
668 assert_eq!(suffix2.as_str(), "foo/bar");
669 assert_eq!(suffix3.as_str(), "foo/bar");
670 }
671
672 #[test]
673 fn test_path_types_basic_operations() {
674 let prefix = Path::new("foo/bar");
675 assert_eq!(prefix.as_str(), "foo/bar");
676 assert!(!prefix.is_empty());
677 assert_eq!(prefix.len(), 7);
678
679 let suffix = Path::new("baz/qux");
680 assert_eq!(suffix.as_str(), "baz/qux");
681 assert!(!suffix.is_empty());
682 assert_eq!(suffix.len(), 7);
683
684 let empty_prefix = Path::new("");
685 assert!(empty_prefix.is_empty());
686 assert_eq!(empty_prefix.len(), 0);
687
688 let empty_suffix = Path::new("");
689 assert!(empty_suffix.is_empty());
690 assert_eq!(empty_suffix.len(), 0);
691 }
692
693 #[test]
694 fn test_prefix_has_prefix() {
695 let prefix = Path::new("foo/bar");
697 assert!(prefix.has_prefix(""));
698
699 let prefix = Path::new("foo/bar");
701 assert!(prefix.has_prefix("foo/bar"));
702
703 assert!(prefix.has_prefix("foo"));
705 assert!(prefix.has_prefix("foo/"));
706
707 assert!(!prefix.has_prefix("f"));
709 assert!(!prefix.has_prefix("fo"));
710 assert!(!prefix.has_prefix("foo/b"));
711 assert!(!prefix.has_prefix("foo/ba"));
712
713 let prefix = Path::new("foobar");
715 assert!(!prefix.has_prefix("foo"));
716 assert!(prefix.has_prefix("foobar"));
717
718 let prefix = Path::new("foo/bar/");
720 assert!(prefix.has_prefix("foo"));
721 assert!(prefix.has_prefix("foo/"));
722 assert!(prefix.has_prefix("foo/bar"));
723 assert!(prefix.has_prefix("foo/bar/"));
724
725 let prefix = Path::new("foo");
727 assert!(prefix.has_prefix(""));
728 assert!(prefix.has_prefix("foo"));
729 assert!(prefix.has_prefix("foo/")); assert!(!prefix.has_prefix("f"));
731
732 let prefix = Path::new("");
734 assert!(prefix.has_prefix(""));
735 assert!(!prefix.has_prefix("foo"));
736 }
737
738 #[test]
739 fn test_prefix_join() {
740 let prefix = Path::new("foo");
742 let suffix = Path::new("bar");
743 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
744
745 let prefix = Path::new("foo/");
747 let suffix = Path::new("bar");
748 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
749
750 let prefix = Path::new("foo");
752 let suffix = Path::new("/bar");
753 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
754
755 let prefix = Path::new("foo");
757 let suffix = Path::new("bar/");
758 assert_eq!(prefix.join(suffix).as_str(), "foo/bar"); let prefix = Path::new("foo/");
762 let suffix = Path::new("/bar");
763 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
764
765 let prefix = Path::new("foo");
767 let suffix = Path::new("");
768 assert_eq!(prefix.join(suffix).as_str(), "foo");
769
770 let prefix = Path::new("");
772 let suffix = Path::new("bar");
773 assert_eq!(prefix.join(suffix).as_str(), "bar");
774
775 let prefix = Path::new("");
777 let suffix = Path::new("");
778 assert_eq!(prefix.join(suffix).as_str(), "");
779
780 let prefix = Path::new("foo/bar");
782 let suffix = Path::new("baz/qux");
783 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux");
784
785 let prefix = Path::new("foo/bar/");
787 let suffix = Path::new("/baz/qux/");
788 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux"); }
790
791 #[test]
792 fn test_path_ref() {
793 let ref1 = Path::new("/foo/bar/");
795 assert_eq!(ref1.as_str(), "foo/bar");
796
797 let ref2 = Path::from("///foo///");
798 assert_eq!(ref2.as_str(), "foo");
799
800 let ref3 = Path::new("foo//bar///baz");
802 assert_eq!(ref3.as_str(), "foo/bar/baz");
803
804 let path = Path::new("foo/bar");
806 let path_ref = path;
807 assert_eq!(path_ref.as_str(), "foo/bar");
808
809 let path2 = Path::new("foo/bar/baz");
811 assert!(path2.has_prefix(&path_ref));
812 assert_eq!(path2.strip_prefix(path_ref).unwrap().as_str(), "baz");
813
814 let empty = Path::new("");
816 assert!(empty.is_empty());
817 assert_eq!(empty.len(), 0);
818 }
819
820 #[test]
821 fn test_multiple_consecutive_slashes() {
822 let path = Path::new("foo//bar///baz");
823 assert_eq!(path.as_str(), "foo/bar/baz");
825
826 let path2 = Path::new("//foo//bar///baz//");
828 assert_eq!(path2.as_str(), "foo/bar/baz");
829
830 let path3 = Path::new("foo///bar");
832 assert_eq!(path3.as_str(), "foo/bar");
833 }
834
835 #[test]
836 fn test_removes_multiple_slashes_comprehensively() {
837 assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
839 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
840 assert_eq!(Path::new("foo////bar").as_str(), "foo/bar");
841
842 assert_eq!(Path::new("foo//bar//baz").as_str(), "foo/bar/baz");
844 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
845
846 assert_eq!(Path::new("foo//bar///baz////qux").as_str(), "foo/bar/baz/qux");
848
849 assert_eq!(Path::new("//foo//bar//").as_str(), "foo/bar");
851 assert_eq!(Path::new("///foo///bar///").as_str(), "foo/bar");
852
853 assert_eq!(Path::new("//").as_str(), "");
855 assert_eq!(Path::new("////").as_str(), "");
856
857 let path_with_slashes = Path::new("foo//bar///baz");
859 assert!(path_with_slashes.has_prefix("foo/bar"));
860 assert_eq!(path_with_slashes.strip_prefix("foo").unwrap().as_str(), "bar/baz");
861 assert_eq!(path_with_slashes.join("qux").as_str(), "foo/bar/baz/qux");
862
863 let path_ref = Path::new("foo//bar///baz");
865 assert_eq!(path_ref.as_str(), "foo/bar/baz"); let path_from_ref = path_ref.to_owned();
867 assert_eq!(path_from_ref.as_str(), "foo/bar/baz"); }
869
870 #[test]
871 fn test_path_ref_multiple_slashes() {
872 let path_ref = Path::new("//foo//bar///baz//");
874 assert_eq!(path_ref.as_str(), "foo/bar/baz"); assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
878 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
879 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
880
881 assert_eq!(Path::new("foo//bar").to_owned().as_str(), "foo/bar");
883 assert_eq!(Path::new("foo///bar").to_owned().as_str(), "foo/bar");
884 assert_eq!(Path::new("a//b//c//d").to_owned().as_str(), "a/b/c/d");
885
886 assert_eq!(Path::new("//").as_str(), "");
888 assert_eq!(Path::new("////").as_str(), "");
889 assert_eq!(Path::new("//").to_owned().as_str(), "");
890 assert_eq!(Path::new("////").to_owned().as_str(), "");
891
892 let normal_path = Path::new("foo/bar/baz");
894 assert_eq!(normal_path.as_str(), "foo/bar/baz");
895 let needs_norm = Path::new("foo//bar");
898 assert_eq!(needs_norm.as_str(), "foo/bar");
899 }
901
902 #[test]
903 fn test_ergonomic_conversions() {
904 fn takes_path_ref<'a>(p: impl Into<Path<'a>>) -> String {
906 p.into().as_str().to_string()
907 }
908
909 fn takes_path_ref_with_trait<'a>(p: impl Into<Path<'a>>) -> String {
911 p.into().as_str().to_string()
912 }
913
914 assert_eq!(takes_path_ref("foo//bar"), "foo/bar");
916
917 let owned_string = String::from("foo//bar///baz");
919 assert_eq!(takes_path_ref(owned_string), "foo/bar/baz");
920
921 let string_ref = String::from("foo//bar");
923 assert_eq!(takes_path_ref(string_ref), "foo/bar");
924
925 let path_ref = Path::new("foo//bar");
927 assert_eq!(takes_path_ref(path_ref), "foo/bar");
928
929 let path = Path::new("foo//bar");
931 assert_eq!(takes_path_ref(path), "foo/bar");
932
933 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");
941 assert_eq!(takes_path_ref_with_trait(String::from("foo//bar")), "foo/bar");
942 }
943
944 #[test]
945 fn test_prefix_strip_prefix() {
946 let prefix = Path::new("foo/bar/baz");
948 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
949 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar/baz");
950 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar/baz");
951 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "baz");
952 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "baz");
953 assert_eq!(prefix.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
954
955 assert!(prefix.strip_prefix("fo").is_none());
957 assert!(prefix.strip_prefix("bar").is_none());
958 assert!(prefix.strip_prefix("foo/ba").is_none());
959
960 let prefix = Path::new("foobar");
962 assert!(prefix.strip_prefix("foo").is_none());
963 assert_eq!(prefix.strip_prefix("foobar").unwrap().as_str(), "");
964
965 let prefix = Path::new("");
967 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "");
968 assert!(prefix.strip_prefix("foo").is_none());
969
970 let prefix = Path::new("foo");
972 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "");
973 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), ""); let prefix = Path::new("foo/bar/");
977 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar");
978 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar");
979 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "");
980 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "");
981 }
982
983 #[test]
984 fn test_prefix_list_dedup() {
985 let list = PathPrefixes::new(["demo", "demo"]);
987 assert_eq!(list.len(), 1);
988 assert_eq!(list[0], Path::new("demo"));
989 }
990
991 #[test]
992 fn test_prefix_list_overlap() {
993 let list = PathPrefixes::new(["demo", "demo/foo", "anon"]);
995 assert_eq!(list.len(), 2);
996 assert!(list.iter().any(|p| p == &Path::new("demo")));
997 assert!(list.iter().any(|p| p == &Path::new("anon")));
998 }
999
1000 #[test]
1001 fn test_prefix_list_overlap_reverse_order() {
1002 let list = PathPrefixes::new(["demo/foo", "demo"]);
1004 assert_eq!(list.len(), 1);
1005 assert_eq!(list[0], Path::new("demo"));
1006 }
1007
1008 #[test]
1009 fn test_prefix_list_empty_covers_all() {
1010 let list = PathPrefixes::new(["", "demo", "anon"]);
1012 assert_eq!(list.len(), 1);
1013 assert_eq!(list[0], Path::new(""));
1014 }
1015
1016 #[test]
1017 fn test_prefix_list_no_overlap() {
1018 let list = PathPrefixes::new(["demo", "anon", "secret"]);
1020 assert_eq!(list.len(), 3);
1021 }
1022
1023 #[test]
1024 fn test_prefix_list_single() {
1025 let list = PathPrefixes::new(["demo"]);
1026 assert_eq!(list.len(), 1);
1027 }
1028
1029 #[test]
1030 fn test_prefix_list_empty() {
1031 let list = PathPrefixes::new(std::iter::empty::<&str>());
1032 assert!(list.is_empty());
1033 assert_eq!(list.len(), 0);
1034 }
1035
1036 #[test]
1037 fn test_prefix_list_deep_overlap() {
1038 let list = PathPrefixes::new(["a/b/c", "a/b", "a"]);
1040 assert_eq!(list.len(), 1);
1041 assert_eq!(list[0], Path::new("a"));
1042 }
1043
1044 #[test]
1045 fn test_prefix_list_partial_name_not_overlap() {
1046 let list = PathPrefixes::new(["demo", "demonstration"]);
1048 assert_eq!(list.len(), 2);
1049 }
1050
1051 #[test]
1052 fn test_prefix_list_collect() {
1053 let paths: Vec<PathOwned> = vec!["demo".into(), "demo/foo".into()];
1054 let list: PathPrefixes = paths.into_iter().collect();
1055 assert_eq!(list.len(), 1);
1056 assert_eq!(list[0], Path::new("demo"));
1057 }
1058
1059 #[test]
1060 fn test_prefix_list_eq_vec() {
1061 let list = PathPrefixes::new(["demo", "anon"]);
1062 assert_eq!(list, vec!["anon".as_path(), "demo".as_path()]);
1064 }
1065
1066 #[test]
1069 fn test_owned_paths_share_allocation() {
1070 let path = Path::new("customer/room/broadcast").to_owned();
1071
1072 let cloned = path.clone();
1074 assert_eq!(path.as_str().as_ptr(), cloned.as_str().as_ptr());
1075
1076 let requeued = path.as_path().to_owned();
1078 assert_eq!(path.as_str().as_ptr(), requeued.as_str().as_ptr());
1079
1080 let stripped = path.strip_prefix("customer").unwrap().to_owned();
1082 assert_eq!(stripped.as_str(), "room/broadcast");
1083 assert_eq!(stripped.as_str().as_ptr(), path.as_str()["customer/".len()..].as_ptr());
1084
1085 let (dir, rest) = path.next_part().unwrap();
1087 assert_eq!(dir, "customer");
1088 let rest = rest.to_owned();
1089 assert_eq!(rest.as_str().as_ptr(), stripped.as_str().as_ptr());
1090
1091 let joined = path.join("alice");
1093 let joined2 = joined.clone();
1094 assert_eq!(joined.as_str(), "customer/room/broadcast/alice");
1095 assert_eq!(joined.as_str().as_ptr(), joined2.as_str().as_ptr());
1096 }
1097
1098 #[test]
1099 fn test_parts() {
1100 assert_eq!(Path::empty().parts().count(), 0);
1101 assert_eq!(Path::new("foo").parts().collect::<Vec<_>>(), ["foo"]);
1102 assert_eq!(Path::new("/foo//bar/").parts().collect::<Vec<_>>(), ["foo", "bar"]);
1103 }
1104
1105 #[test]
1106 fn test_wire_max_parts() {
1107 use crate::lite::Version;
1108
1109 let ok = (0..Path::MAX_PARTS)
1110 .map(|i| i.to_string())
1111 .collect::<Vec<_>>()
1112 .join("/");
1113 let too_deep = format!("{ok}/extra");
1114
1115 let mut buf = bytes::BytesMut::new();
1117 Path::new(&ok).encode(&mut buf, Version::Lite04).unwrap();
1118 assert!(matches!(
1119 Path::new(&too_deep).encode(&mut bytes::BytesMut::new(), Version::Lite04),
1120 Err(EncodeError::BoundsExceeded)
1121 ));
1122
1123 let decoded = Path::decode(&mut buf.freeze(), Version::Lite04).unwrap();
1125 assert_eq!(decoded.as_str(), ok);
1126
1127 let mut buf = bytes::BytesMut::new();
1129 too_deep.as_str().encode(&mut buf, Version::Lite04).unwrap();
1130 assert!(matches!(
1131 Path::decode(&mut buf.freeze(), Version::Lite04),
1132 Err(DecodeError::BoundsExceeded)
1133 ));
1134 }
1135
1136 #[test]
1137 fn test_owned_empty_paths() {
1138 let empty = Path::new("").to_owned();
1140 assert!(empty.is_empty());
1141 assert_eq!(empty, Path::empty());
1142
1143 let path = Path::new("foo").to_owned();
1144 let rest = path.strip_prefix("foo").unwrap().to_owned();
1145 assert!(rest.is_empty());
1146 }
1147
1148 #[test]
1149 fn test_prefix_list_canonical_order() {
1150 let a = PathPrefixes::new(["foo", "bar"]);
1152 let b = PathPrefixes::new(["bar", "foo"]);
1153 assert_eq!(a, b);
1154 }
1155}