1use std::borrow::Cow;
2use std::fmt::{self, Display};
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 Path(Cow::Borrowed(self.as_str()))
27 }
28}
29
30impl AsPath for Path<'_> {
31 fn as_path(&self) -> Path<'_> {
32 Path(Cow::Borrowed(self.0.as_ref()))
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(Debug, PartialEq, Eq, Hash, Clone)]
75#[cfg_attr(feature = "serde", derive(serde::Serialize))]
76pub struct Path<'a>(Cow<'a, str>);
77
78impl<'a> Path<'a> {
79 pub fn new(s: &'a str) -> Self {
84 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
85
86 if trimmed.contains("//") {
88 let normalized = trimmed
90 .split('/')
91 .filter(|s| !s.is_empty())
92 .collect::<Vec<_>>()
93 .join("/");
94 Self(Cow::Owned(normalized))
95 } else {
96 Self(Cow::Borrowed(trimmed))
98 }
99 }
100
101 pub fn has_prefix(&self, prefix: impl AsPath) -> bool {
123 let prefix = prefix.as_path();
124
125 if prefix.is_empty() {
126 return true;
127 }
128
129 if !self.0.starts_with(prefix.as_str()) {
130 return false;
131 }
132
133 if self.0.len() == prefix.len() {
135 return true;
136 }
137
138 self.0.chars().nth(prefix.len()) == Some('/')
140 }
141
142 pub fn strip_prefix(&'a self, prefix: impl AsPath) -> Option<Path<'a>> {
143 let prefix = prefix.as_path();
144
145 if prefix.is_empty() {
146 return Some(self.borrow());
147 }
148
149 if !self.0.starts_with(prefix.as_str()) {
150 return None;
151 }
152
153 if self.0.len() == prefix.len() {
155 return Some(Path(Cow::Borrowed("")));
156 }
157
158 if self.0.chars().nth(prefix.len()) != Some('/') {
160 return None;
161 }
162
163 Some(Path(Cow::Borrowed(&self.0[prefix.len() + 1..])))
164 }
165
166 pub fn next_part(&'a self) -> Option<(&'a str, Path<'a>)> {
168 if self.0.is_empty() {
169 return None;
170 }
171
172 if let Some(i) = self.0.find('/') {
173 let dir = &self.0[..i];
174 let rest = Path(Cow::Borrowed(&self.0[i + 1..]));
175 Some((dir, rest))
176 } else {
177 Some((&self.0, Path(Cow::Borrowed(""))))
178 }
179 }
180
181 pub fn as_str(&self) -> &str {
182 &self.0
183 }
184
185 pub fn empty() -> Path<'static> {
186 Path(Cow::Borrowed(""))
187 }
188
189 pub fn is_empty(&self) -> bool {
190 self.0.is_empty()
191 }
192
193 pub fn len(&self) -> usize {
194 self.0.len()
195 }
196
197 pub fn to_owned(&self) -> PathOwned {
198 Path(Cow::Owned(self.0.to_string()))
199 }
200
201 pub fn into_owned(self) -> PathOwned {
202 Path(Cow::Owned(self.0.to_string()))
203 }
204
205 pub fn borrow(&'a self) -> Path<'a> {
206 Path(Cow::Borrowed(&self.0))
207 }
208
209 pub fn join(&self, other: impl AsPath) -> PathOwned {
223 let other = other.as_path();
224
225 if self.0.is_empty() {
226 Path(Cow::Owned(other.0.to_string()))
227 } else if other.is_empty() {
228 self.to_owned()
230 } else {
231 Path(Cow::Owned(format!("{}/{}", self.0, other.as_str())))
233 }
234 }
235}
236
237impl<'a> From<&'a str> for Path<'a> {
238 fn from(s: &'a str) -> Self {
239 Self::new(s)
240 }
241}
242
243impl<'a> From<&'a String> for Path<'a> {
244 fn from(s: &'a String) -> Self {
245 Self::new(s)
247 }
248}
249
250impl Default for Path<'_> {
251 fn default() -> Self {
252 Self(Cow::Borrowed(""))
253 }
254}
255
256impl From<String> for Path<'_> {
257 fn from(s: String) -> Self {
258 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
260
261 if trimmed.contains("//") {
263 let normalized = trimmed
265 .split('/')
266 .filter(|s| !s.is_empty())
267 .collect::<Vec<_>>()
268 .join("/");
269 Self(Cow::Owned(normalized))
270 } else if trimmed == s {
271 Self(Cow::Owned(s))
273 } else {
274 Self(Cow::Owned(trimmed.to_string()))
276 }
277 }
278}
279
280impl AsRef<str> for Path<'_> {
281 fn as_ref(&self) -> &str {
282 &self.0
283 }
284}
285
286impl Display for Path<'_> {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 write!(f, "{}", self.0)
289 }
290}
291
292impl<V> Decode<V> for Path<'_> {
293 fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
294 Ok(String::decode(r, version)?.into())
295 }
296}
297
298impl<V> Encode<V> for Path<'_> {
299 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
300 self.as_str().encode(w, version)?;
301 Ok(())
302 }
303}
304
305#[cfg(feature = "serde")]
307impl<'de: 'a, 'a> serde::Deserialize<'de> for Path<'a> {
308 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
309 where
310 D: serde::Deserializer<'de>,
311 {
312 let s = <&'a str as serde::Deserialize<'de>>::deserialize(deserializer)?;
313 Ok(Path::new(s))
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320
321 #[test]
322 fn test_has_prefix() {
323 let path = Path::new("foo/bar/baz");
324
325 assert!(path.has_prefix(""));
327 assert!(path.has_prefix("foo"));
328 assert!(path.has_prefix(Path::new("foo")));
329 assert!(path.has_prefix("foo/"));
330 assert!(path.has_prefix("foo/bar"));
331 assert!(path.has_prefix(Path::new("foo/bar/")));
332 assert!(path.has_prefix("foo/bar/baz"));
333
334 assert!(!path.has_prefix("f"));
336 assert!(!path.has_prefix(Path::new("fo")));
337 assert!(!path.has_prefix("foo/b"));
338 assert!(!path.has_prefix("foo/ba"));
339 assert!(!path.has_prefix(Path::new("foo/bar/ba")));
340
341 let path = Path::new("foobar");
343 assert!(!path.has_prefix("foo"));
344 assert!(path.has_prefix(Path::new("foobar")));
345 }
346
347 #[test]
348 fn test_strip_prefix() {
349 let path = Path::new("foo/bar/baz");
350
351 assert_eq!(path.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
353 assert_eq!(path.strip_prefix("foo").unwrap().as_str(), "bar/baz");
354 assert_eq!(path.strip_prefix(Path::new("foo/")).unwrap().as_str(), "bar/baz");
355 assert_eq!(path.strip_prefix("foo/bar").unwrap().as_str(), "baz");
356 assert_eq!(path.strip_prefix(Path::new("foo/bar/")).unwrap().as_str(), "baz");
357 assert_eq!(path.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
358
359 assert!(path.strip_prefix("fo").is_none());
361 assert!(path.strip_prefix(Path::new("bar")).is_none());
362 }
363
364 #[test]
365 fn test_join() {
366 assert_eq!(Path::new("foo").join("bar").as_str(), "foo/bar");
368 assert_eq!(Path::new("foo/").join(Path::new("bar")).as_str(), "foo/bar");
369 assert_eq!(Path::new("").join("bar").as_str(), "bar");
370 assert_eq!(Path::new("foo/bar").join(Path::new("baz")).as_str(), "foo/bar/baz");
371 }
372
373 #[test]
374 fn test_empty() {
375 let empty = Path::new("");
376 assert!(empty.is_empty());
377 assert_eq!(empty.len(), 0);
378
379 let non_empty = Path::new("foo");
380 assert!(!non_empty.is_empty());
381 assert_eq!(non_empty.len(), 3);
382 }
383
384 #[test]
385 fn test_from_conversions() {
386 let path1 = Path::from("foo/bar");
387 let path2 = Path::from("foo/bar");
388 let s = String::from("foo/bar");
389 let path3 = Path::from(&s);
390
391 assert_eq!(path1.as_str(), "foo/bar");
392 assert_eq!(path2.as_str(), "foo/bar");
393 assert_eq!(path3.as_str(), "foo/bar");
394 }
395
396 #[test]
397 fn test_path_prefix_join() {
398 let prefix = Path::new("foo");
399 let suffix = Path::new("bar/baz");
400 let path = prefix.join(&suffix);
401 assert_eq!(path.as_str(), "foo/bar/baz");
402
403 let prefix = Path::new("foo/");
404 let suffix = Path::new("bar/baz");
405 let path = prefix.join(&suffix);
406 assert_eq!(path.as_str(), "foo/bar/baz");
407
408 let prefix = Path::new("foo");
409 let suffix = Path::new("/bar/baz");
410 let path = prefix.join(&suffix);
411 assert_eq!(path.as_str(), "foo/bar/baz");
412
413 let prefix = Path::new("");
414 let suffix = Path::new("bar/baz");
415 let path = prefix.join(&suffix);
416 assert_eq!(path.as_str(), "bar/baz");
417 }
418
419 #[test]
420 fn test_path_prefix_conversions() {
421 let prefix1 = Path::from("foo/bar");
422 let prefix2 = Path::from(String::from("foo/bar"));
423 let s = String::from("foo/bar");
424 let prefix3 = Path::from(&s);
425
426 assert_eq!(prefix1.as_str(), "foo/bar");
427 assert_eq!(prefix2.as_str(), "foo/bar");
428 assert_eq!(prefix3.as_str(), "foo/bar");
429 }
430
431 #[test]
432 fn test_path_suffix_conversions() {
433 let suffix1 = Path::from("foo/bar");
434 let suffix2 = Path::from(String::from("foo/bar"));
435 let s = String::from("foo/bar");
436 let suffix3 = Path::from(&s);
437
438 assert_eq!(suffix1.as_str(), "foo/bar");
439 assert_eq!(suffix2.as_str(), "foo/bar");
440 assert_eq!(suffix3.as_str(), "foo/bar");
441 }
442
443 #[test]
444 fn test_path_types_basic_operations() {
445 let prefix = Path::new("foo/bar");
446 assert_eq!(prefix.as_str(), "foo/bar");
447 assert!(!prefix.is_empty());
448 assert_eq!(prefix.len(), 7);
449
450 let suffix = Path::new("baz/qux");
451 assert_eq!(suffix.as_str(), "baz/qux");
452 assert!(!suffix.is_empty());
453 assert_eq!(suffix.len(), 7);
454
455 let empty_prefix = Path::new("");
456 assert!(empty_prefix.is_empty());
457 assert_eq!(empty_prefix.len(), 0);
458
459 let empty_suffix = Path::new("");
460 assert!(empty_suffix.is_empty());
461 assert_eq!(empty_suffix.len(), 0);
462 }
463
464 #[test]
465 fn test_prefix_has_prefix() {
466 let prefix = Path::new("foo/bar");
468 assert!(prefix.has_prefix(""));
469
470 let prefix = Path::new("foo/bar");
472 assert!(prefix.has_prefix("foo/bar"));
473
474 assert!(prefix.has_prefix("foo"));
476 assert!(prefix.has_prefix("foo/"));
477
478 assert!(!prefix.has_prefix("f"));
480 assert!(!prefix.has_prefix("fo"));
481 assert!(!prefix.has_prefix("foo/b"));
482 assert!(!prefix.has_prefix("foo/ba"));
483
484 let prefix = Path::new("foobar");
486 assert!(!prefix.has_prefix("foo"));
487 assert!(prefix.has_prefix("foobar"));
488
489 let prefix = Path::new("foo/bar/");
491 assert!(prefix.has_prefix("foo"));
492 assert!(prefix.has_prefix("foo/"));
493 assert!(prefix.has_prefix("foo/bar"));
494 assert!(prefix.has_prefix("foo/bar/"));
495
496 let prefix = Path::new("foo");
498 assert!(prefix.has_prefix(""));
499 assert!(prefix.has_prefix("foo"));
500 assert!(prefix.has_prefix("foo/")); assert!(!prefix.has_prefix("f"));
502
503 let prefix = Path::new("");
505 assert!(prefix.has_prefix(""));
506 assert!(!prefix.has_prefix("foo"));
507 }
508
509 #[test]
510 fn test_prefix_join() {
511 let prefix = Path::new("foo");
513 let suffix = Path::new("bar");
514 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
515
516 let prefix = Path::new("foo/");
518 let suffix = Path::new("bar");
519 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
520
521 let prefix = Path::new("foo");
523 let suffix = Path::new("/bar");
524 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
525
526 let prefix = Path::new("foo");
528 let suffix = Path::new("bar/");
529 assert_eq!(prefix.join(suffix).as_str(), "foo/bar"); let prefix = Path::new("foo/");
533 let suffix = Path::new("/bar");
534 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
535
536 let prefix = Path::new("foo");
538 let suffix = Path::new("");
539 assert_eq!(prefix.join(suffix).as_str(), "foo");
540
541 let prefix = Path::new("");
543 let suffix = Path::new("bar");
544 assert_eq!(prefix.join(suffix).as_str(), "bar");
545
546 let prefix = Path::new("");
548 let suffix = Path::new("");
549 assert_eq!(prefix.join(suffix).as_str(), "");
550
551 let prefix = Path::new("foo/bar");
553 let suffix = Path::new("baz/qux");
554 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux");
555
556 let prefix = Path::new("foo/bar/");
558 let suffix = Path::new("/baz/qux/");
559 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux"); }
561
562 #[test]
563 fn test_path_ref() {
564 let ref1 = Path::new("/foo/bar/");
566 assert_eq!(ref1.as_str(), "foo/bar");
567
568 let ref2 = Path::from("///foo///");
569 assert_eq!(ref2.as_str(), "foo");
570
571 let ref3 = Path::new("foo//bar///baz");
573 assert_eq!(ref3.as_str(), "foo/bar/baz");
574
575 let path = Path::new("foo/bar");
577 let path_ref = path;
578 assert_eq!(path_ref.as_str(), "foo/bar");
579
580 let path2 = Path::new("foo/bar/baz");
582 assert!(path2.has_prefix(&path_ref));
583 assert_eq!(path2.strip_prefix(path_ref).unwrap().as_str(), "baz");
584
585 let empty = Path::new("");
587 assert!(empty.is_empty());
588 assert_eq!(empty.len(), 0);
589 }
590
591 #[test]
592 fn test_multiple_consecutive_slashes() {
593 let path = Path::new("foo//bar///baz");
594 assert_eq!(path.as_str(), "foo/bar/baz");
596
597 let path2 = Path::new("//foo//bar///baz//");
599 assert_eq!(path2.as_str(), "foo/bar/baz");
600
601 let path3 = Path::new("foo///bar");
603 assert_eq!(path3.as_str(), "foo/bar");
604 }
605
606 #[test]
607 fn test_removes_multiple_slashes_comprehensively() {
608 assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
610 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
611 assert_eq!(Path::new("foo////bar").as_str(), "foo/bar");
612
613 assert_eq!(Path::new("foo//bar//baz").as_str(), "foo/bar/baz");
615 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
616
617 assert_eq!(Path::new("foo//bar///baz////qux").as_str(), "foo/bar/baz/qux");
619
620 assert_eq!(Path::new("//foo//bar//").as_str(), "foo/bar");
622 assert_eq!(Path::new("///foo///bar///").as_str(), "foo/bar");
623
624 assert_eq!(Path::new("//").as_str(), "");
626 assert_eq!(Path::new("////").as_str(), "");
627
628 let path_with_slashes = Path::new("foo//bar///baz");
630 assert!(path_with_slashes.has_prefix("foo/bar"));
631 assert_eq!(path_with_slashes.strip_prefix("foo").unwrap().as_str(), "bar/baz");
632 assert_eq!(path_with_slashes.join("qux").as_str(), "foo/bar/baz/qux");
633
634 let path_ref = Path::new("foo//bar///baz");
636 assert_eq!(path_ref.as_str(), "foo/bar/baz"); let path_from_ref = path_ref.to_owned();
638 assert_eq!(path_from_ref.as_str(), "foo/bar/baz"); }
640
641 #[test]
642 fn test_path_ref_multiple_slashes() {
643 let path_ref = Path::new("//foo//bar///baz//");
645 assert_eq!(path_ref.as_str(), "foo/bar/baz"); assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
649 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
650 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
651
652 assert_eq!(Path::new("foo//bar").to_owned().as_str(), "foo/bar");
654 assert_eq!(Path::new("foo///bar").to_owned().as_str(), "foo/bar");
655 assert_eq!(Path::new("a//b//c//d").to_owned().as_str(), "a/b/c/d");
656
657 assert_eq!(Path::new("//").as_str(), "");
659 assert_eq!(Path::new("////").as_str(), "");
660 assert_eq!(Path::new("//").to_owned().as_str(), "");
661 assert_eq!(Path::new("////").to_owned().as_str(), "");
662
663 let normal_path = Path::new("foo/bar/baz");
665 assert_eq!(normal_path.as_str(), "foo/bar/baz");
666 let needs_norm = Path::new("foo//bar");
669 assert_eq!(needs_norm.as_str(), "foo/bar");
670 }
672
673 #[test]
674 fn test_ergonomic_conversions() {
675 fn takes_path_ref<'a>(p: impl Into<Path<'a>>) -> String {
677 p.into().as_str().to_string()
678 }
679
680 fn takes_path_ref_with_trait<'a>(p: impl Into<Path<'a>>) -> String {
682 p.into().as_str().to_string()
683 }
684
685 assert_eq!(takes_path_ref("foo//bar"), "foo/bar");
687
688 let owned_string = String::from("foo//bar///baz");
690 assert_eq!(takes_path_ref(owned_string), "foo/bar/baz");
691
692 let string_ref = String::from("foo//bar");
694 assert_eq!(takes_path_ref(string_ref), "foo/bar");
695
696 let path_ref = Path::new("foo//bar");
698 assert_eq!(takes_path_ref(path_ref), "foo/bar");
699
700 let path = Path::new("foo//bar");
702 assert_eq!(takes_path_ref(path), "foo/bar");
703
704 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");
712 assert_eq!(takes_path_ref_with_trait(String::from("foo//bar")), "foo/bar");
713 }
714
715 #[test]
716 fn test_prefix_strip_prefix() {
717 let prefix = Path::new("foo/bar/baz");
719 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
720 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar/baz");
721 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar/baz");
722 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "baz");
723 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "baz");
724 assert_eq!(prefix.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
725
726 assert!(prefix.strip_prefix("fo").is_none());
728 assert!(prefix.strip_prefix("bar").is_none());
729 assert!(prefix.strip_prefix("foo/ba").is_none());
730
731 let prefix = Path::new("foobar");
733 assert!(prefix.strip_prefix("foo").is_none());
734 assert_eq!(prefix.strip_prefix("foobar").unwrap().as_str(), "");
735
736 let prefix = Path::new("");
738 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "");
739 assert!(prefix.strip_prefix("foo").is_none());
740
741 let prefix = Path::new("foo");
743 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "");
744 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), ""); let prefix = Path::new("foo/bar/");
748 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar");
749 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar");
750 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "");
751 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "");
752 }
753}