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 is_empty(&self) -> bool {
185 self.0.is_empty()
186 }
187
188 pub fn len(&self) -> usize {
189 self.0.len()
190 }
191
192 pub fn to_owned(&self) -> PathOwned {
193 Path(Cow::Owned(self.0.to_string()))
194 }
195
196 pub fn into_owned(self) -> PathOwned {
197 Path(Cow::Owned(self.0.to_string()))
198 }
199
200 pub fn borrow(&'a self) -> Path<'a> {
201 Path(Cow::Borrowed(&self.0))
202 }
203
204 pub fn join(&self, other: impl AsPath) -> PathOwned {
218 let other = other.as_path();
219
220 if self.0.is_empty() {
221 Path(Cow::Owned(other.0.to_string()))
222 } else if other.is_empty() {
223 self.to_owned()
225 } else {
226 Path(Cow::Owned(format!("{}/{}", self.0, other.as_str())))
228 }
229 }
230}
231
232impl<'a> From<&'a str> for Path<'a> {
233 fn from(s: &'a str) -> Self {
234 Self::new(s)
235 }
236}
237
238impl<'a> From<&'a String> for Path<'a> {
239 fn from(s: &'a String) -> Self {
240 Self::new(s)
242 }
243}
244
245impl<'a> Default for Path<'a> {
246 fn default() -> Self {
247 Self(Cow::Borrowed(""))
248 }
249}
250
251impl<'a> From<String> for Path<'a> {
252 fn from(s: String) -> Self {
253 let trimmed = s.trim_start_matches('/').trim_end_matches('/');
255
256 if trimmed.contains("//") {
258 let normalized = trimmed
260 .split('/')
261 .filter(|s| !s.is_empty())
262 .collect::<Vec<_>>()
263 .join("/");
264 Self(Cow::Owned(normalized))
265 } else if trimmed == s {
266 Self(Cow::Owned(s))
268 } else {
269 Self(Cow::Owned(trimmed.to_string()))
271 }
272 }
273}
274
275impl<'a> AsRef<str> for Path<'a> {
276 fn as_ref(&self) -> &str {
277 &self.0
278 }
279}
280
281impl<'a> Display for Path<'a> {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 write!(f, "{}", self.0)
284 }
285}
286
287impl<'a> Decode for Path<'a> {
288 fn decode<R: bytes::Buf>(r: &mut R) -> Result<Self, DecodeError> {
289 Ok(String::decode(r)?.into())
290 }
291}
292
293impl<'a> Encode for Path<'a> {
294 fn encode<W: bytes::BufMut>(&self, w: &mut W) {
295 self.as_str().encode(w)
296 }
297}
298
299#[cfg(feature = "serde")]
301impl<'de: 'a, 'a> serde::Deserialize<'de> for Path<'a> {
302 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
303 where
304 D: serde::Deserializer<'de>,
305 {
306 let s = <&'a str as serde::Deserialize<'de>>::deserialize(deserializer)?;
307 Ok(Path::new(s))
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use super::*;
314
315 #[test]
316 fn test_has_prefix() {
317 let path = Path::new("foo/bar/baz");
318
319 assert!(path.has_prefix(""));
321 assert!(path.has_prefix("foo"));
322 assert!(path.has_prefix(Path::new("foo")));
323 assert!(path.has_prefix("foo/"));
324 assert!(path.has_prefix("foo/bar"));
325 assert!(path.has_prefix(Path::new("foo/bar/")));
326 assert!(path.has_prefix("foo/bar/baz"));
327
328 assert!(!path.has_prefix("f"));
330 assert!(!path.has_prefix(Path::new("fo")));
331 assert!(!path.has_prefix("foo/b"));
332 assert!(!path.has_prefix("foo/ba"));
333 assert!(!path.has_prefix(Path::new("foo/bar/ba")));
334
335 let path = Path::new("foobar");
337 assert!(!path.has_prefix("foo"));
338 assert!(path.has_prefix(Path::new("foobar")));
339 }
340
341 #[test]
342 fn test_strip_prefix() {
343 let path = Path::new("foo/bar/baz");
344
345 assert_eq!(path.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
347 assert_eq!(path.strip_prefix("foo").unwrap().as_str(), "bar/baz");
348 assert_eq!(path.strip_prefix(Path::new("foo/")).unwrap().as_str(), "bar/baz");
349 assert_eq!(path.strip_prefix("foo/bar").unwrap().as_str(), "baz");
350 assert_eq!(path.strip_prefix(Path::new("foo/bar/")).unwrap().as_str(), "baz");
351 assert_eq!(path.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
352
353 assert!(path.strip_prefix("fo").is_none());
355 assert!(path.strip_prefix(Path::new("bar")).is_none());
356 }
357
358 #[test]
359 fn test_join() {
360 assert_eq!(Path::new("foo").join("bar").as_str(), "foo/bar");
362 assert_eq!(Path::new("foo/").join(Path::new("bar")).as_str(), "foo/bar");
363 assert_eq!(Path::new("").join("bar").as_str(), "bar");
364 assert_eq!(Path::new("foo/bar").join(Path::new("baz")).as_str(), "foo/bar/baz");
365 }
366
367 #[test]
368 fn test_empty() {
369 let empty = Path::new("");
370 assert!(empty.is_empty());
371 assert_eq!(empty.len(), 0);
372
373 let non_empty = Path::new("foo");
374 assert!(!non_empty.is_empty());
375 assert_eq!(non_empty.len(), 3);
376 }
377
378 #[test]
379 fn test_from_conversions() {
380 let path1 = Path::from("foo/bar");
381 let path2 = Path::from("foo/bar");
382 let s = String::from("foo/bar");
383 let path3 = Path::from(&s);
384
385 assert_eq!(path1.as_str(), "foo/bar");
386 assert_eq!(path2.as_str(), "foo/bar");
387 assert_eq!(path3.as_str(), "foo/bar");
388 }
389
390 #[test]
391 fn test_path_prefix_join() {
392 let prefix = Path::new("foo");
393 let suffix = Path::new("bar/baz");
394 let path = prefix.join(&suffix);
395 assert_eq!(path.as_str(), "foo/bar/baz");
396
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("");
408 let suffix = Path::new("bar/baz");
409 let path = prefix.join(&suffix);
410 assert_eq!(path.as_str(), "bar/baz");
411 }
412
413 #[test]
414 fn test_path_prefix_conversions() {
415 let prefix1 = Path::from("foo/bar");
416 let prefix2 = Path::from(String::from("foo/bar"));
417 let s = String::from("foo/bar");
418 let prefix3 = Path::from(&s);
419
420 assert_eq!(prefix1.as_str(), "foo/bar");
421 assert_eq!(prefix2.as_str(), "foo/bar");
422 assert_eq!(prefix3.as_str(), "foo/bar");
423 }
424
425 #[test]
426 fn test_path_suffix_conversions() {
427 let suffix1 = Path::from("foo/bar");
428 let suffix2 = Path::from(String::from("foo/bar"));
429 let s = String::from("foo/bar");
430 let suffix3 = Path::from(&s);
431
432 assert_eq!(suffix1.as_str(), "foo/bar");
433 assert_eq!(suffix2.as_str(), "foo/bar");
434 assert_eq!(suffix3.as_str(), "foo/bar");
435 }
436
437 #[test]
438 fn test_path_types_basic_operations() {
439 let prefix = Path::new("foo/bar");
440 assert_eq!(prefix.as_str(), "foo/bar");
441 assert!(!prefix.is_empty());
442 assert_eq!(prefix.len(), 7);
443
444 let suffix = Path::new("baz/qux");
445 assert_eq!(suffix.as_str(), "baz/qux");
446 assert!(!suffix.is_empty());
447 assert_eq!(suffix.len(), 7);
448
449 let empty_prefix = Path::new("");
450 assert!(empty_prefix.is_empty());
451 assert_eq!(empty_prefix.len(), 0);
452
453 let empty_suffix = Path::new("");
454 assert!(empty_suffix.is_empty());
455 assert_eq!(empty_suffix.len(), 0);
456 }
457
458 #[test]
459 fn test_prefix_has_prefix() {
460 let prefix = Path::new("foo/bar");
462 assert!(prefix.has_prefix(""));
463
464 let prefix = Path::new("foo/bar");
466 assert!(prefix.has_prefix("foo/bar"));
467
468 assert!(prefix.has_prefix("foo"));
470 assert!(prefix.has_prefix("foo/"));
471
472 assert!(!prefix.has_prefix("f"));
474 assert!(!prefix.has_prefix("fo"));
475 assert!(!prefix.has_prefix("foo/b"));
476 assert!(!prefix.has_prefix("foo/ba"));
477
478 let prefix = Path::new("foobar");
480 assert!(!prefix.has_prefix("foo"));
481 assert!(prefix.has_prefix("foobar"));
482
483 let prefix = Path::new("foo/bar/");
485 assert!(prefix.has_prefix("foo"));
486 assert!(prefix.has_prefix("foo/"));
487 assert!(prefix.has_prefix("foo/bar"));
488 assert!(prefix.has_prefix("foo/bar/"));
489
490 let prefix = Path::new("foo");
492 assert!(prefix.has_prefix(""));
493 assert!(prefix.has_prefix("foo"));
494 assert!(prefix.has_prefix("foo/")); assert!(!prefix.has_prefix("f"));
496
497 let prefix = Path::new("");
499 assert!(prefix.has_prefix(""));
500 assert!(!prefix.has_prefix("foo"));
501 }
502
503 #[test]
504 fn test_prefix_join() {
505 let prefix = Path::new("foo");
507 let suffix = Path::new("bar");
508 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
509
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"); let prefix = Path::new("foo/");
527 let suffix = Path::new("/bar");
528 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
529
530 let prefix = Path::new("foo");
532 let suffix = Path::new("");
533 assert_eq!(prefix.join(suffix).as_str(), "foo");
534
535 let prefix = Path::new("");
537 let suffix = Path::new("bar");
538 assert_eq!(prefix.join(suffix).as_str(), "bar");
539
540 let prefix = Path::new("");
542 let suffix = Path::new("");
543 assert_eq!(prefix.join(suffix).as_str(), "");
544
545 let prefix = Path::new("foo/bar");
547 let suffix = Path::new("baz/qux");
548 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux");
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"); }
555
556 #[test]
557 fn test_path_ref() {
558 let ref1 = Path::new("/foo/bar/");
560 assert_eq!(ref1.as_str(), "foo/bar");
561
562 let ref2 = Path::from("///foo///");
563 assert_eq!(ref2.as_str(), "foo");
564
565 let ref3 = Path::new("foo//bar///baz");
567 assert_eq!(ref3.as_str(), "foo/bar/baz");
568
569 let path = Path::new("foo/bar");
571 let path_ref = path;
572 assert_eq!(path_ref.as_str(), "foo/bar");
573
574 let path2 = Path::new("foo/bar/baz");
576 assert!(path2.has_prefix(&path_ref));
577 assert_eq!(path2.strip_prefix(path_ref).unwrap().as_str(), "baz");
578
579 let empty = Path::new("");
581 assert!(empty.is_empty());
582 assert_eq!(empty.len(), 0);
583 }
584
585 #[test]
586 fn test_multiple_consecutive_slashes() {
587 let path = Path::new("foo//bar///baz");
588 assert_eq!(path.as_str(), "foo/bar/baz");
590
591 let path2 = Path::new("//foo//bar///baz//");
593 assert_eq!(path2.as_str(), "foo/bar/baz");
594
595 let path3 = Path::new("foo///bar");
597 assert_eq!(path3.as_str(), "foo/bar");
598 }
599
600 #[test]
601 fn test_removes_multiple_slashes_comprehensively() {
602 assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
604 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
605 assert_eq!(Path::new("foo////bar").as_str(), "foo/bar");
606
607 assert_eq!(Path::new("foo//bar//baz").as_str(), "foo/bar/baz");
609 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
610
611 assert_eq!(Path::new("foo//bar///baz////qux").as_str(), "foo/bar/baz/qux");
613
614 assert_eq!(Path::new("//foo//bar//").as_str(), "foo/bar");
616 assert_eq!(Path::new("///foo///bar///").as_str(), "foo/bar");
617
618 assert_eq!(Path::new("//").as_str(), "");
620 assert_eq!(Path::new("////").as_str(), "");
621
622 let path_with_slashes = Path::new("foo//bar///baz");
624 assert!(path_with_slashes.has_prefix("foo/bar"));
625 assert_eq!(path_with_slashes.strip_prefix("foo").unwrap().as_str(), "bar/baz");
626 assert_eq!(path_with_slashes.join("qux").as_str(), "foo/bar/baz/qux");
627
628 let path_ref = Path::new("foo//bar///baz");
630 assert_eq!(path_ref.as_str(), "foo/bar/baz"); let path_from_ref = path_ref.to_owned();
632 assert_eq!(path_from_ref.as_str(), "foo/bar/baz"); }
634
635 #[test]
636 fn test_path_ref_multiple_slashes() {
637 let path_ref = Path::new("//foo//bar///baz//");
639 assert_eq!(path_ref.as_str(), "foo/bar/baz"); assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
643 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
644 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
645
646 assert_eq!(Path::new("foo//bar").to_owned().as_str(), "foo/bar");
648 assert_eq!(Path::new("foo///bar").to_owned().as_str(), "foo/bar");
649 assert_eq!(Path::new("a//b//c//d").to_owned().as_str(), "a/b/c/d");
650
651 assert_eq!(Path::new("//").as_str(), "");
653 assert_eq!(Path::new("////").as_str(), "");
654 assert_eq!(Path::new("//").to_owned().as_str(), "");
655 assert_eq!(Path::new("////").to_owned().as_str(), "");
656
657 let normal_path = Path::new("foo/bar/baz");
659 assert_eq!(normal_path.as_str(), "foo/bar/baz");
660 let needs_norm = Path::new("foo//bar");
663 assert_eq!(needs_norm.as_str(), "foo/bar");
664 }
666
667 #[test]
668 fn test_ergonomic_conversions() {
669 fn takes_path_ref<'a>(p: impl Into<Path<'a>>) -> String {
671 p.into().as_str().to_string()
672 }
673
674 fn takes_path_ref_with_trait<'a>(p: impl Into<Path<'a>>) -> String {
676 p.into().as_str().to_string()
677 }
678
679 assert_eq!(takes_path_ref("foo//bar"), "foo/bar");
681
682 let owned_string = String::from("foo//bar///baz");
684 assert_eq!(takes_path_ref(owned_string), "foo/bar/baz");
685
686 let string_ref = String::from("foo//bar");
688 assert_eq!(takes_path_ref(string_ref), "foo/bar");
689
690 let path_ref = Path::new("foo//bar");
692 assert_eq!(takes_path_ref(path_ref), "foo/bar");
693
694 let path = Path::new("foo//bar");
696 assert_eq!(takes_path_ref(path), "foo/bar");
697
698 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");
706 assert_eq!(takes_path_ref_with_trait(String::from("foo//bar")), "foo/bar");
707 }
708
709 #[test]
710 fn test_prefix_strip_prefix() {
711 let prefix = Path::new("foo/bar/baz");
713 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
714 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar/baz");
715 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar/baz");
716 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "baz");
717 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "baz");
718 assert_eq!(prefix.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
719
720 assert!(prefix.strip_prefix("fo").is_none());
722 assert!(prefix.strip_prefix("bar").is_none());
723 assert!(prefix.strip_prefix("foo/ba").is_none());
724
725 let prefix = Path::new("foobar");
727 assert!(prefix.strip_prefix("foo").is_none());
728 assert_eq!(prefix.strip_prefix("foobar").unwrap().as_str(), "");
729
730 let prefix = Path::new("");
732 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "");
733 assert!(prefix.strip_prefix("foo").is_none());
734
735 let prefix = Path::new("foo");
737 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "");
738 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), ""); let prefix = Path::new("foo/bar/");
742 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar");
743 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar");
744 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "");
745 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "");
746 }
747}