1use std::borrow::Cow;
2use std::fmt::{self, Display};
3
4use crate::coding::{Decode, DecodeError, Encode};
5
6pub type PathOwned = Path<'static>;
7
8pub trait AsPath {
13 fn as_path(&self) -> Path<'_>;
14}
15
16impl<'a> AsPath for &'a str {
17 fn as_path(&self) -> Path<'a> {
18 Path::new(self)
19 }
20}
21
22impl<'a> AsPath for &'a Path<'a> {
23 fn as_path(&self) -> Path<'a> {
24 Path(Cow::Borrowed(self.as_str()))
26 }
27}
28
29impl<'a> AsPath for Path<'a> {
30 fn as_path(&self) -> Path<'_> {
31 Path(Cow::Borrowed(self.0.as_ref()))
32 }
33}
34
35impl AsPath for String {
36 fn as_path(&self) -> Path<'_> {
37 Path(Cow::Borrowed(self))
38 }
39}
40
41impl<'a> AsPath for &'a String {
42 fn as_path(&self) -> Path<'a> {
43 Path(Cow::Borrowed(self))
44 }
45}
46
47#[derive(Debug, PartialEq, Eq, Hash, Clone)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize))]
75pub struct Path<'a>(Cow<'a, str>);
76
77impl<'a> Path<'a> {
78 pub fn new(s: &'a str) -> Self {
83 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
84
85 if trimmed.contains("//") {
87 let normalized = trimmed
89 .split('/')
90 .filter(|s| !s.is_empty())
91 .collect::<Vec<_>>()
92 .join("/");
93 Self(Cow::Owned(normalized))
94 } else {
95 Self(Cow::Borrowed(trimmed))
97 }
98 }
99
100 pub fn has_prefix(&self, prefix: impl AsPath) -> bool {
122 let prefix = prefix.as_path();
123
124 if prefix.is_empty() {
125 return true;
126 }
127
128 if !self.0.starts_with(prefix.as_str()) {
129 return false;
130 }
131
132 if self.0.len() == prefix.len() {
134 return true;
135 }
136
137 self.0.chars().nth(prefix.len()) == Some('/')
139 }
140
141 pub fn strip_prefix(&'a self, prefix: impl AsPath) -> Option<Path<'a>> {
142 let prefix = prefix.as_path();
143
144 if prefix.is_empty() {
145 return Some(self.borrow());
146 }
147
148 if !self.0.starts_with(prefix.as_str()) {
149 return None;
150 }
151
152 if self.0.len() == prefix.len() {
154 return Some(Path(Cow::Borrowed("")));
155 }
156
157 if self.0.chars().nth(prefix.len()) != Some('/') {
159 return None;
160 }
161
162 Some(Path(Cow::Borrowed(&self.0[prefix.len() + 1..])))
163 }
164
165 pub fn next_part(&'a self) -> Option<(&'a str, Path<'a>)> {
167 if self.0.is_empty() {
168 return None;
169 }
170
171 if let Some(i) = self.0.find('/') {
172 let dir = &self.0[..i];
173 let rest = Path(Cow::Borrowed(&self.0[i + 1..]));
174 Some((dir, rest))
175 } else {
176 Some((&self.0, Path(Cow::Borrowed(""))))
177 }
178 }
179
180 pub fn as_str(&self) -> &str {
181 &self.0
182 }
183
184 pub fn empty() -> Path<'static> {
185 Path(Cow::Borrowed(""))
186 }
187
188 pub fn is_empty(&self) -> bool {
189 self.0.is_empty()
190 }
191
192 pub fn len(&self) -> usize {
193 self.0.len()
194 }
195
196 pub fn to_owned(&self) -> PathOwned {
197 Path(Cow::Owned(self.0.to_string()))
198 }
199
200 pub fn into_owned(self) -> PathOwned {
201 Path(Cow::Owned(self.0.to_string()))
202 }
203
204 pub fn borrow(&'a self) -> Path<'a> {
205 Path(Cow::Borrowed(&self.0))
206 }
207
208 pub fn join(&self, other: impl AsPath) -> PathOwned {
222 let other = other.as_path();
223
224 if self.0.is_empty() {
225 Path(Cow::Owned(other.0.to_string()))
226 } else if other.is_empty() {
227 self.to_owned()
229 } else {
230 Path(Cow::Owned(format!("{}/{}", self.0, other.as_str())))
232 }
233 }
234}
235
236impl<'a> From<&'a str> for Path<'a> {
237 fn from(s: &'a str) -> Self {
238 Self::new(s)
239 }
240}
241
242impl<'a> From<&'a String> for Path<'a> {
243 fn from(s: &'a String) -> Self {
244 Self::new(s)
246 }
247}
248
249impl<'a> Default for Path<'a> {
250 fn default() -> Self {
251 Self(Cow::Borrowed(""))
252 }
253}
254
255impl<'a> From<String> for Path<'a> {
256 fn from(s: String) -> Self {
257 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
259
260 if trimmed.contains("//") {
262 let normalized = trimmed
264 .split('/')
265 .filter(|s| !s.is_empty())
266 .collect::<Vec<_>>()
267 .join("/");
268 Self(Cow::Owned(normalized))
269 } else if trimmed == s {
270 Self(Cow::Owned(s))
272 } else {
273 Self(Cow::Owned(trimmed.to_string()))
275 }
276 }
277}
278
279impl<'a> AsRef<str> for Path<'a> {
280 fn as_ref(&self) -> &str {
281 &self.0
282 }
283}
284
285impl<'a> Display for Path<'a> {
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 write!(f, "{}", self.0)
288 }
289}
290
291impl<'a, V> Decode<V> for Path<'a> {
292 fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
293 Ok(String::decode(r, version)?.into())
294 }
295}
296
297impl<'a, V> Encode<V> for Path<'a> {
298 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) {
299 self.as_str().encode(w, version)
300 }
301}
302
303#[cfg(feature = "serde")]
305impl<'de: 'a, 'a> serde::Deserialize<'de> for Path<'a> {
306 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
307 where
308 D: serde::Deserializer<'de>,
309 {
310 let s = <&'a str as serde::Deserialize<'de>>::deserialize(deserializer)?;
311 Ok(Path::new(s))
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn test_has_prefix() {
321 let path = Path::new("foo/bar/baz");
322
323 assert!(path.has_prefix(""));
325 assert!(path.has_prefix("foo"));
326 assert!(path.has_prefix(Path::new("foo")));
327 assert!(path.has_prefix("foo/"));
328 assert!(path.has_prefix("foo/bar"));
329 assert!(path.has_prefix(Path::new("foo/bar/")));
330 assert!(path.has_prefix("foo/bar/baz"));
331
332 assert!(!path.has_prefix("f"));
334 assert!(!path.has_prefix(Path::new("fo")));
335 assert!(!path.has_prefix("foo/b"));
336 assert!(!path.has_prefix("foo/ba"));
337 assert!(!path.has_prefix(Path::new("foo/bar/ba")));
338
339 let path = Path::new("foobar");
341 assert!(!path.has_prefix("foo"));
342 assert!(path.has_prefix(Path::new("foobar")));
343 }
344
345 #[test]
346 fn test_strip_prefix() {
347 let path = Path::new("foo/bar/baz");
348
349 assert_eq!(path.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
351 assert_eq!(path.strip_prefix("foo").unwrap().as_str(), "bar/baz");
352 assert_eq!(path.strip_prefix(Path::new("foo/")).unwrap().as_str(), "bar/baz");
353 assert_eq!(path.strip_prefix("foo/bar").unwrap().as_str(), "baz");
354 assert_eq!(path.strip_prefix(Path::new("foo/bar/")).unwrap().as_str(), "baz");
355 assert_eq!(path.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
356
357 assert!(path.strip_prefix("fo").is_none());
359 assert!(path.strip_prefix(Path::new("bar")).is_none());
360 }
361
362 #[test]
363 fn test_join() {
364 assert_eq!(Path::new("foo").join("bar").as_str(), "foo/bar");
366 assert_eq!(Path::new("foo/").join(Path::new("bar")).as_str(), "foo/bar");
367 assert_eq!(Path::new("").join("bar").as_str(), "bar");
368 assert_eq!(Path::new("foo/bar").join(Path::new("baz")).as_str(), "foo/bar/baz");
369 }
370
371 #[test]
372 fn test_empty() {
373 let empty = Path::new("");
374 assert!(empty.is_empty());
375 assert_eq!(empty.len(), 0);
376
377 let non_empty = Path::new("foo");
378 assert!(!non_empty.is_empty());
379 assert_eq!(non_empty.len(), 3);
380 }
381
382 #[test]
383 fn test_from_conversions() {
384 let path1 = Path::from("foo/bar");
385 let path2 = Path::from("foo/bar");
386 let s = String::from("foo/bar");
387 let path3 = Path::from(&s);
388
389 assert_eq!(path1.as_str(), "foo/bar");
390 assert_eq!(path2.as_str(), "foo/bar");
391 assert_eq!(path3.as_str(), "foo/bar");
392 }
393
394 #[test]
395 fn test_path_prefix_join() {
396 let prefix = Path::new("foo");
397 let suffix = Path::new("bar/baz");
398 let path = prefix.join(&suffix);
399 assert_eq!(path.as_str(), "foo/bar/baz");
400
401 let prefix = Path::new("foo/");
402 let suffix = Path::new("bar/baz");
403 let path = prefix.join(&suffix);
404 assert_eq!(path.as_str(), "foo/bar/baz");
405
406 let prefix = Path::new("foo");
407 let suffix = Path::new("/bar/baz");
408 let path = prefix.join(&suffix);
409 assert_eq!(path.as_str(), "foo/bar/baz");
410
411 let prefix = Path::new("");
412 let suffix = Path::new("bar/baz");
413 let path = prefix.join(&suffix);
414 assert_eq!(path.as_str(), "bar/baz");
415 }
416
417 #[test]
418 fn test_path_prefix_conversions() {
419 let prefix1 = Path::from("foo/bar");
420 let prefix2 = Path::from(String::from("foo/bar"));
421 let s = String::from("foo/bar");
422 let prefix3 = Path::from(&s);
423
424 assert_eq!(prefix1.as_str(), "foo/bar");
425 assert_eq!(prefix2.as_str(), "foo/bar");
426 assert_eq!(prefix3.as_str(), "foo/bar");
427 }
428
429 #[test]
430 fn test_path_suffix_conversions() {
431 let suffix1 = Path::from("foo/bar");
432 let suffix2 = Path::from(String::from("foo/bar"));
433 let s = String::from("foo/bar");
434 let suffix3 = Path::from(&s);
435
436 assert_eq!(suffix1.as_str(), "foo/bar");
437 assert_eq!(suffix2.as_str(), "foo/bar");
438 assert_eq!(suffix3.as_str(), "foo/bar");
439 }
440
441 #[test]
442 fn test_path_types_basic_operations() {
443 let prefix = Path::new("foo/bar");
444 assert_eq!(prefix.as_str(), "foo/bar");
445 assert!(!prefix.is_empty());
446 assert_eq!(prefix.len(), 7);
447
448 let suffix = Path::new("baz/qux");
449 assert_eq!(suffix.as_str(), "baz/qux");
450 assert!(!suffix.is_empty());
451 assert_eq!(suffix.len(), 7);
452
453 let empty_prefix = Path::new("");
454 assert!(empty_prefix.is_empty());
455 assert_eq!(empty_prefix.len(), 0);
456
457 let empty_suffix = Path::new("");
458 assert!(empty_suffix.is_empty());
459 assert_eq!(empty_suffix.len(), 0);
460 }
461
462 #[test]
463 fn test_prefix_has_prefix() {
464 let prefix = Path::new("foo/bar");
466 assert!(prefix.has_prefix(""));
467
468 let prefix = Path::new("foo/bar");
470 assert!(prefix.has_prefix("foo/bar"));
471
472 assert!(prefix.has_prefix("foo"));
474 assert!(prefix.has_prefix("foo/"));
475
476 assert!(!prefix.has_prefix("f"));
478 assert!(!prefix.has_prefix("fo"));
479 assert!(!prefix.has_prefix("foo/b"));
480 assert!(!prefix.has_prefix("foo/ba"));
481
482 let prefix = Path::new("foobar");
484 assert!(!prefix.has_prefix("foo"));
485 assert!(prefix.has_prefix("foobar"));
486
487 let prefix = Path::new("foo/bar/");
489 assert!(prefix.has_prefix("foo"));
490 assert!(prefix.has_prefix("foo/"));
491 assert!(prefix.has_prefix("foo/bar"));
492 assert!(prefix.has_prefix("foo/bar/"));
493
494 let prefix = Path::new("foo");
496 assert!(prefix.has_prefix(""));
497 assert!(prefix.has_prefix("foo"));
498 assert!(prefix.has_prefix("foo/")); assert!(!prefix.has_prefix("f"));
500
501 let prefix = Path::new("");
503 assert!(prefix.has_prefix(""));
504 assert!(!prefix.has_prefix("foo"));
505 }
506
507 #[test]
508 fn test_prefix_join() {
509 let prefix = Path::new("foo");
511 let suffix = Path::new("bar");
512 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
513
514 let prefix = Path::new("foo/");
516 let suffix = Path::new("bar");
517 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
518
519 let prefix = Path::new("foo");
521 let suffix = Path::new("/bar");
522 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
523
524 let prefix = Path::new("foo");
526 let suffix = Path::new("bar/");
527 assert_eq!(prefix.join(suffix).as_str(), "foo/bar"); let prefix = Path::new("foo/");
531 let suffix = Path::new("/bar");
532 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
533
534 let prefix = Path::new("foo");
536 let suffix = Path::new("");
537 assert_eq!(prefix.join(suffix).as_str(), "foo");
538
539 let prefix = Path::new("");
541 let suffix = Path::new("bar");
542 assert_eq!(prefix.join(suffix).as_str(), "bar");
543
544 let prefix = Path::new("");
546 let suffix = Path::new("");
547 assert_eq!(prefix.join(suffix).as_str(), "");
548
549 let prefix = Path::new("foo/bar");
551 let suffix = Path::new("baz/qux");
552 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux");
553
554 let prefix = Path::new("foo/bar/");
556 let suffix = Path::new("/baz/qux/");
557 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux"); }
559
560 #[test]
561 fn test_path_ref() {
562 let ref1 = Path::new("/foo/bar/");
564 assert_eq!(ref1.as_str(), "foo/bar");
565
566 let ref2 = Path::from("///foo///");
567 assert_eq!(ref2.as_str(), "foo");
568
569 let ref3 = Path::new("foo//bar///baz");
571 assert_eq!(ref3.as_str(), "foo/bar/baz");
572
573 let path = Path::new("foo/bar");
575 let path_ref = path;
576 assert_eq!(path_ref.as_str(), "foo/bar");
577
578 let path2 = Path::new("foo/bar/baz");
580 assert!(path2.has_prefix(&path_ref));
581 assert_eq!(path2.strip_prefix(path_ref).unwrap().as_str(), "baz");
582
583 let empty = Path::new("");
585 assert!(empty.is_empty());
586 assert_eq!(empty.len(), 0);
587 }
588
589 #[test]
590 fn test_multiple_consecutive_slashes() {
591 let path = Path::new("foo//bar///baz");
592 assert_eq!(path.as_str(), "foo/bar/baz");
594
595 let path2 = Path::new("//foo//bar///baz//");
597 assert_eq!(path2.as_str(), "foo/bar/baz");
598
599 let path3 = Path::new("foo///bar");
601 assert_eq!(path3.as_str(), "foo/bar");
602 }
603
604 #[test]
605 fn test_removes_multiple_slashes_comprehensively() {
606 assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
608 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
609 assert_eq!(Path::new("foo////bar").as_str(), "foo/bar");
610
611 assert_eq!(Path::new("foo//bar//baz").as_str(), "foo/bar/baz");
613 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
614
615 assert_eq!(Path::new("foo//bar///baz////qux").as_str(), "foo/bar/baz/qux");
617
618 assert_eq!(Path::new("//foo//bar//").as_str(), "foo/bar");
620 assert_eq!(Path::new("///foo///bar///").as_str(), "foo/bar");
621
622 assert_eq!(Path::new("//").as_str(), "");
624 assert_eq!(Path::new("////").as_str(), "");
625
626 let path_with_slashes = Path::new("foo//bar///baz");
628 assert!(path_with_slashes.has_prefix("foo/bar"));
629 assert_eq!(path_with_slashes.strip_prefix("foo").unwrap().as_str(), "bar/baz");
630 assert_eq!(path_with_slashes.join("qux").as_str(), "foo/bar/baz/qux");
631
632 let path_ref = Path::new("foo//bar///baz");
634 assert_eq!(path_ref.as_str(), "foo/bar/baz"); let path_from_ref = path_ref.to_owned();
636 assert_eq!(path_from_ref.as_str(), "foo/bar/baz"); }
638
639 #[test]
640 fn test_path_ref_multiple_slashes() {
641 let path_ref = Path::new("//foo//bar///baz//");
643 assert_eq!(path_ref.as_str(), "foo/bar/baz"); assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
647 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
648 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
649
650 assert_eq!(Path::new("foo//bar").to_owned().as_str(), "foo/bar");
652 assert_eq!(Path::new("foo///bar").to_owned().as_str(), "foo/bar");
653 assert_eq!(Path::new("a//b//c//d").to_owned().as_str(), "a/b/c/d");
654
655 assert_eq!(Path::new("//").as_str(), "");
657 assert_eq!(Path::new("////").as_str(), "");
658 assert_eq!(Path::new("//").to_owned().as_str(), "");
659 assert_eq!(Path::new("////").to_owned().as_str(), "");
660
661 let normal_path = Path::new("foo/bar/baz");
663 assert_eq!(normal_path.as_str(), "foo/bar/baz");
664 let needs_norm = Path::new("foo//bar");
667 assert_eq!(needs_norm.as_str(), "foo/bar");
668 }
670
671 #[test]
672 fn test_ergonomic_conversions() {
673 fn takes_path_ref<'a>(p: impl Into<Path<'a>>) -> String {
675 p.into().as_str().to_string()
676 }
677
678 fn takes_path_ref_with_trait<'a>(p: impl Into<Path<'a>>) -> String {
680 p.into().as_str().to_string()
681 }
682
683 assert_eq!(takes_path_ref("foo//bar"), "foo/bar");
685
686 let owned_string = String::from("foo//bar///baz");
688 assert_eq!(takes_path_ref(owned_string), "foo/bar/baz");
689
690 let string_ref = String::from("foo//bar");
692 assert_eq!(takes_path_ref(string_ref), "foo/bar");
693
694 let path_ref = Path::new("foo//bar");
696 assert_eq!(takes_path_ref(path_ref), "foo/bar");
697
698 let path = Path::new("foo//bar");
700 assert_eq!(takes_path_ref(path), "foo/bar");
701
702 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");
710 assert_eq!(takes_path_ref_with_trait(String::from("foo//bar")), "foo/bar");
711 }
712
713 #[test]
714 fn test_prefix_strip_prefix() {
715 let prefix = Path::new("foo/bar/baz");
717 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
718 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar/baz");
719 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar/baz");
720 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "baz");
721 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "baz");
722 assert_eq!(prefix.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
723
724 assert!(prefix.strip_prefix("fo").is_none());
726 assert!(prefix.strip_prefix("bar").is_none());
727 assert!(prefix.strip_prefix("foo/ba").is_none());
728
729 let prefix = Path::new("foobar");
731 assert!(prefix.strip_prefix("foo").is_none());
732 assert_eq!(prefix.strip_prefix("foobar").unwrap().as_str(), "");
733
734 let prefix = Path::new("");
736 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "");
737 assert!(prefix.strip_prefix("foo").is_none());
738
739 let prefix = Path::new("foo");
741 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "");
742 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), ""); let prefix = Path::new("foo/bar/");
746 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar");
747 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar");
748 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "");
749 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "");
750 }
751}