1use std::borrow::Cow;
2use std::fmt::{self, Display};
3
4use crate::coding::{Decode, DecodeError, Encode};
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(Cow::Borrowed(self))
39 }
40}
41
42impl<'a> AsPath for &'a String {
43 fn as_path(&self) -> Path<'a> {
44 Path(Cow::Borrowed(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) {
300 self.as_str().encode(w, version)
301 }
302}
303
304#[cfg(feature = "serde")]
306impl<'de: 'a, 'a> serde::Deserialize<'de> for Path<'a> {
307 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
308 where
309 D: serde::Deserializer<'de>,
310 {
311 let s = <&'a str as serde::Deserialize<'de>>::deserialize(deserializer)?;
312 Ok(Path::new(s))
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319
320 #[test]
321 fn test_has_prefix() {
322 let path = Path::new("foo/bar/baz");
323
324 assert!(path.has_prefix(""));
326 assert!(path.has_prefix("foo"));
327 assert!(path.has_prefix(Path::new("foo")));
328 assert!(path.has_prefix("foo/"));
329 assert!(path.has_prefix("foo/bar"));
330 assert!(path.has_prefix(Path::new("foo/bar/")));
331 assert!(path.has_prefix("foo/bar/baz"));
332
333 assert!(!path.has_prefix("f"));
335 assert!(!path.has_prefix(Path::new("fo")));
336 assert!(!path.has_prefix("foo/b"));
337 assert!(!path.has_prefix("foo/ba"));
338 assert!(!path.has_prefix(Path::new("foo/bar/ba")));
339
340 let path = Path::new("foobar");
342 assert!(!path.has_prefix("foo"));
343 assert!(path.has_prefix(Path::new("foobar")));
344 }
345
346 #[test]
347 fn test_strip_prefix() {
348 let path = Path::new("foo/bar/baz");
349
350 assert_eq!(path.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
352 assert_eq!(path.strip_prefix("foo").unwrap().as_str(), "bar/baz");
353 assert_eq!(path.strip_prefix(Path::new("foo/")).unwrap().as_str(), "bar/baz");
354 assert_eq!(path.strip_prefix("foo/bar").unwrap().as_str(), "baz");
355 assert_eq!(path.strip_prefix(Path::new("foo/bar/")).unwrap().as_str(), "baz");
356 assert_eq!(path.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
357
358 assert!(path.strip_prefix("fo").is_none());
360 assert!(path.strip_prefix(Path::new("bar")).is_none());
361 }
362
363 #[test]
364 fn test_join() {
365 assert_eq!(Path::new("foo").join("bar").as_str(), "foo/bar");
367 assert_eq!(Path::new("foo/").join(Path::new("bar")).as_str(), "foo/bar");
368 assert_eq!(Path::new("").join("bar").as_str(), "bar");
369 assert_eq!(Path::new("foo/bar").join(Path::new("baz")).as_str(), "foo/bar/baz");
370 }
371
372 #[test]
373 fn test_empty() {
374 let empty = Path::new("");
375 assert!(empty.is_empty());
376 assert_eq!(empty.len(), 0);
377
378 let non_empty = Path::new("foo");
379 assert!(!non_empty.is_empty());
380 assert_eq!(non_empty.len(), 3);
381 }
382
383 #[test]
384 fn test_from_conversions() {
385 let path1 = Path::from("foo/bar");
386 let path2 = Path::from("foo/bar");
387 let s = String::from("foo/bar");
388 let path3 = Path::from(&s);
389
390 assert_eq!(path1.as_str(), "foo/bar");
391 assert_eq!(path2.as_str(), "foo/bar");
392 assert_eq!(path3.as_str(), "foo/bar");
393 }
394
395 #[test]
396 fn test_path_prefix_join() {
397 let prefix = Path::new("foo");
398 let suffix = Path::new("bar/baz");
399 let path = prefix.join(&suffix);
400 assert_eq!(path.as_str(), "foo/bar/baz");
401
402 let prefix = Path::new("foo/");
403 let suffix = Path::new("bar/baz");
404 let path = prefix.join(&suffix);
405 assert_eq!(path.as_str(), "foo/bar/baz");
406
407 let prefix = Path::new("foo");
408 let suffix = Path::new("/bar/baz");
409 let path = prefix.join(&suffix);
410 assert_eq!(path.as_str(), "foo/bar/baz");
411
412 let prefix = Path::new("");
413 let suffix = Path::new("bar/baz");
414 let path = prefix.join(&suffix);
415 assert_eq!(path.as_str(), "bar/baz");
416 }
417
418 #[test]
419 fn test_path_prefix_conversions() {
420 let prefix1 = Path::from("foo/bar");
421 let prefix2 = Path::from(String::from("foo/bar"));
422 let s = String::from("foo/bar");
423 let prefix3 = Path::from(&s);
424
425 assert_eq!(prefix1.as_str(), "foo/bar");
426 assert_eq!(prefix2.as_str(), "foo/bar");
427 assert_eq!(prefix3.as_str(), "foo/bar");
428 }
429
430 #[test]
431 fn test_path_suffix_conversions() {
432 let suffix1 = Path::from("foo/bar");
433 let suffix2 = Path::from(String::from("foo/bar"));
434 let s = String::from("foo/bar");
435 let suffix3 = Path::from(&s);
436
437 assert_eq!(suffix1.as_str(), "foo/bar");
438 assert_eq!(suffix2.as_str(), "foo/bar");
439 assert_eq!(suffix3.as_str(), "foo/bar");
440 }
441
442 #[test]
443 fn test_path_types_basic_operations() {
444 let prefix = Path::new("foo/bar");
445 assert_eq!(prefix.as_str(), "foo/bar");
446 assert!(!prefix.is_empty());
447 assert_eq!(prefix.len(), 7);
448
449 let suffix = Path::new("baz/qux");
450 assert_eq!(suffix.as_str(), "baz/qux");
451 assert!(!suffix.is_empty());
452 assert_eq!(suffix.len(), 7);
453
454 let empty_prefix = Path::new("");
455 assert!(empty_prefix.is_empty());
456 assert_eq!(empty_prefix.len(), 0);
457
458 let empty_suffix = Path::new("");
459 assert!(empty_suffix.is_empty());
460 assert_eq!(empty_suffix.len(), 0);
461 }
462
463 #[test]
464 fn test_prefix_has_prefix() {
465 let prefix = Path::new("foo/bar");
467 assert!(prefix.has_prefix(""));
468
469 let prefix = Path::new("foo/bar");
471 assert!(prefix.has_prefix("foo/bar"));
472
473 assert!(prefix.has_prefix("foo"));
475 assert!(prefix.has_prefix("foo/"));
476
477 assert!(!prefix.has_prefix("f"));
479 assert!(!prefix.has_prefix("fo"));
480 assert!(!prefix.has_prefix("foo/b"));
481 assert!(!prefix.has_prefix("foo/ba"));
482
483 let prefix = Path::new("foobar");
485 assert!(!prefix.has_prefix("foo"));
486 assert!(prefix.has_prefix("foobar"));
487
488 let prefix = Path::new("foo/bar/");
490 assert!(prefix.has_prefix("foo"));
491 assert!(prefix.has_prefix("foo/"));
492 assert!(prefix.has_prefix("foo/bar"));
493 assert!(prefix.has_prefix("foo/bar/"));
494
495 let prefix = Path::new("foo");
497 assert!(prefix.has_prefix(""));
498 assert!(prefix.has_prefix("foo"));
499 assert!(prefix.has_prefix("foo/")); assert!(!prefix.has_prefix("f"));
501
502 let prefix = Path::new("");
504 assert!(prefix.has_prefix(""));
505 assert!(!prefix.has_prefix("foo"));
506 }
507
508 #[test]
509 fn test_prefix_join() {
510 let prefix = Path::new("foo");
512 let suffix = Path::new("bar");
513 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
514
515 let prefix = Path::new("foo/");
517 let suffix = Path::new("bar");
518 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
519
520 let prefix = Path::new("foo");
522 let suffix = Path::new("/bar");
523 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
524
525 let prefix = Path::new("foo");
527 let suffix = Path::new("bar/");
528 assert_eq!(prefix.join(suffix).as_str(), "foo/bar"); let prefix = Path::new("foo/");
532 let suffix = Path::new("/bar");
533 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
534
535 let prefix = Path::new("foo");
537 let suffix = Path::new("");
538 assert_eq!(prefix.join(suffix).as_str(), "foo");
539
540 let prefix = Path::new("");
542 let suffix = Path::new("bar");
543 assert_eq!(prefix.join(suffix).as_str(), "bar");
544
545 let prefix = Path::new("");
547 let suffix = Path::new("");
548 assert_eq!(prefix.join(suffix).as_str(), "");
549
550 let prefix = Path::new("foo/bar");
552 let suffix = Path::new("baz/qux");
553 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux");
554
555 let prefix = Path::new("foo/bar/");
557 let suffix = Path::new("/baz/qux/");
558 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux"); }
560
561 #[test]
562 fn test_path_ref() {
563 let ref1 = Path::new("/foo/bar/");
565 assert_eq!(ref1.as_str(), "foo/bar");
566
567 let ref2 = Path::from("///foo///");
568 assert_eq!(ref2.as_str(), "foo");
569
570 let ref3 = Path::new("foo//bar///baz");
572 assert_eq!(ref3.as_str(), "foo/bar/baz");
573
574 let path = Path::new("foo/bar");
576 let path_ref = path;
577 assert_eq!(path_ref.as_str(), "foo/bar");
578
579 let path2 = Path::new("foo/bar/baz");
581 assert!(path2.has_prefix(&path_ref));
582 assert_eq!(path2.strip_prefix(path_ref).unwrap().as_str(), "baz");
583
584 let empty = Path::new("");
586 assert!(empty.is_empty());
587 assert_eq!(empty.len(), 0);
588 }
589
590 #[test]
591 fn test_multiple_consecutive_slashes() {
592 let path = Path::new("foo//bar///baz");
593 assert_eq!(path.as_str(), "foo/bar/baz");
595
596 let path2 = Path::new("//foo//bar///baz//");
598 assert_eq!(path2.as_str(), "foo/bar/baz");
599
600 let path3 = Path::new("foo///bar");
602 assert_eq!(path3.as_str(), "foo/bar");
603 }
604
605 #[test]
606 fn test_removes_multiple_slashes_comprehensively() {
607 assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
609 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
610 assert_eq!(Path::new("foo////bar").as_str(), "foo/bar");
611
612 assert_eq!(Path::new("foo//bar//baz").as_str(), "foo/bar/baz");
614 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
615
616 assert_eq!(Path::new("foo//bar///baz////qux").as_str(), "foo/bar/baz/qux");
618
619 assert_eq!(Path::new("//foo//bar//").as_str(), "foo/bar");
621 assert_eq!(Path::new("///foo///bar///").as_str(), "foo/bar");
622
623 assert_eq!(Path::new("//").as_str(), "");
625 assert_eq!(Path::new("////").as_str(), "");
626
627 let path_with_slashes = Path::new("foo//bar///baz");
629 assert!(path_with_slashes.has_prefix("foo/bar"));
630 assert_eq!(path_with_slashes.strip_prefix("foo").unwrap().as_str(), "bar/baz");
631 assert_eq!(path_with_slashes.join("qux").as_str(), "foo/bar/baz/qux");
632
633 let path_ref = Path::new("foo//bar///baz");
635 assert_eq!(path_ref.as_str(), "foo/bar/baz"); let path_from_ref = path_ref.to_owned();
637 assert_eq!(path_from_ref.as_str(), "foo/bar/baz"); }
639
640 #[test]
641 fn test_path_ref_multiple_slashes() {
642 let path_ref = Path::new("//foo//bar///baz//");
644 assert_eq!(path_ref.as_str(), "foo/bar/baz"); assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
648 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
649 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
650
651 assert_eq!(Path::new("foo//bar").to_owned().as_str(), "foo/bar");
653 assert_eq!(Path::new("foo///bar").to_owned().as_str(), "foo/bar");
654 assert_eq!(Path::new("a//b//c//d").to_owned().as_str(), "a/b/c/d");
655
656 assert_eq!(Path::new("//").as_str(), "");
658 assert_eq!(Path::new("////").as_str(), "");
659 assert_eq!(Path::new("//").to_owned().as_str(), "");
660 assert_eq!(Path::new("////").to_owned().as_str(), "");
661
662 let normal_path = Path::new("foo/bar/baz");
664 assert_eq!(normal_path.as_str(), "foo/bar/baz");
665 let needs_norm = Path::new("foo//bar");
668 assert_eq!(needs_norm.as_str(), "foo/bar");
669 }
671
672 #[test]
673 fn test_ergonomic_conversions() {
674 fn takes_path_ref<'a>(p: impl Into<Path<'a>>) -> String {
676 p.into().as_str().to_string()
677 }
678
679 fn takes_path_ref_with_trait<'a>(p: impl Into<Path<'a>>) -> String {
681 p.into().as_str().to_string()
682 }
683
684 assert_eq!(takes_path_ref("foo//bar"), "foo/bar");
686
687 let owned_string = String::from("foo//bar///baz");
689 assert_eq!(takes_path_ref(owned_string), "foo/bar/baz");
690
691 let string_ref = String::from("foo//bar");
693 assert_eq!(takes_path_ref(string_ref), "foo/bar");
694
695 let path_ref = Path::new("foo//bar");
697 assert_eq!(takes_path_ref(path_ref), "foo/bar");
698
699 let path = Path::new("foo//bar");
701 assert_eq!(takes_path_ref(path), "foo/bar");
702
703 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");
711 assert_eq!(takes_path_ref_with_trait(String::from("foo//bar")), "foo/bar");
712 }
713
714 #[test]
715 fn test_prefix_strip_prefix() {
716 let prefix = Path::new("foo/bar/baz");
718 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
719 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar/baz");
720 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar/baz");
721 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "baz");
722 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "baz");
723 assert_eq!(prefix.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
724
725 assert!(prefix.strip_prefix("fo").is_none());
727 assert!(prefix.strip_prefix("bar").is_none());
728 assert!(prefix.strip_prefix("foo/ba").is_none());
729
730 let prefix = Path::new("foobar");
732 assert!(prefix.strip_prefix("foo").is_none());
733 assert_eq!(prefix.strip_prefix("foobar").unwrap().as_str(), "");
734
735 let prefix = Path::new("");
737 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "");
738 assert!(prefix.strip_prefix("foo").is_none());
739
740 let prefix = Path::new("foo");
742 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "");
743 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), ""); let prefix = Path::new("foo/bar/");
747 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar");
748 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar");
749 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "");
750 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "");
751 }
752}