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: Copy> Decode<V> for Path<'_>
293where
294 String: Decode<V>,
295{
296 fn decode<R: bytes::Buf>(r: &mut R, version: V) -> Result<Self, DecodeError> {
297 Ok(String::decode(r, version)?.into())
298 }
299}
300
301impl<V: Copy> Encode<V> for Path<'_>
302where
303 for<'a> &'a str: Encode<V>,
304{
305 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: V) -> Result<(), EncodeError> {
306 self.as_str().encode(w, version)?;
307 Ok(())
308 }
309}
310
311#[cfg(feature = "serde")]
313impl<'de: 'a, 'a> serde::Deserialize<'de> for Path<'a> {
314 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
315 where
316 D: serde::Deserializer<'de>,
317 {
318 let s = <&'a str as serde::Deserialize<'de>>::deserialize(deserializer)?;
319 Ok(Path::new(s))
320 }
321}
322
323#[cfg(test)]
324mod tests {
325 use super::*;
326
327 #[test]
328 fn test_has_prefix() {
329 let path = Path::new("foo/bar/baz");
330
331 assert!(path.has_prefix(""));
333 assert!(path.has_prefix("foo"));
334 assert!(path.has_prefix(Path::new("foo")));
335 assert!(path.has_prefix("foo/"));
336 assert!(path.has_prefix("foo/bar"));
337 assert!(path.has_prefix(Path::new("foo/bar/")));
338 assert!(path.has_prefix("foo/bar/baz"));
339
340 assert!(!path.has_prefix("f"));
342 assert!(!path.has_prefix(Path::new("fo")));
343 assert!(!path.has_prefix("foo/b"));
344 assert!(!path.has_prefix("foo/ba"));
345 assert!(!path.has_prefix(Path::new("foo/bar/ba")));
346
347 let path = Path::new("foobar");
349 assert!(!path.has_prefix("foo"));
350 assert!(path.has_prefix(Path::new("foobar")));
351 }
352
353 #[test]
354 fn test_strip_prefix() {
355 let path = Path::new("foo/bar/baz");
356
357 assert_eq!(path.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
359 assert_eq!(path.strip_prefix("foo").unwrap().as_str(), "bar/baz");
360 assert_eq!(path.strip_prefix(Path::new("foo/")).unwrap().as_str(), "bar/baz");
361 assert_eq!(path.strip_prefix("foo/bar").unwrap().as_str(), "baz");
362 assert_eq!(path.strip_prefix(Path::new("foo/bar/")).unwrap().as_str(), "baz");
363 assert_eq!(path.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
364
365 assert!(path.strip_prefix("fo").is_none());
367 assert!(path.strip_prefix(Path::new("bar")).is_none());
368 }
369
370 #[test]
371 fn test_join() {
372 assert_eq!(Path::new("foo").join("bar").as_str(), "foo/bar");
374 assert_eq!(Path::new("foo/").join(Path::new("bar")).as_str(), "foo/bar");
375 assert_eq!(Path::new("").join("bar").as_str(), "bar");
376 assert_eq!(Path::new("foo/bar").join(Path::new("baz")).as_str(), "foo/bar/baz");
377 }
378
379 #[test]
380 fn test_empty() {
381 let empty = Path::new("");
382 assert!(empty.is_empty());
383 assert_eq!(empty.len(), 0);
384
385 let non_empty = Path::new("foo");
386 assert!(!non_empty.is_empty());
387 assert_eq!(non_empty.len(), 3);
388 }
389
390 #[test]
391 fn test_from_conversions() {
392 let path1 = Path::from("foo/bar");
393 let path2 = Path::from("foo/bar");
394 let s = String::from("foo/bar");
395 let path3 = Path::from(&s);
396
397 assert_eq!(path1.as_str(), "foo/bar");
398 assert_eq!(path2.as_str(), "foo/bar");
399 assert_eq!(path3.as_str(), "foo/bar");
400 }
401
402 #[test]
403 fn test_path_prefix_join() {
404 let prefix = Path::new("foo");
405 let suffix = Path::new("bar/baz");
406 let path = prefix.join(&suffix);
407 assert_eq!(path.as_str(), "foo/bar/baz");
408
409 let prefix = Path::new("foo/");
410 let suffix = Path::new("bar/baz");
411 let path = prefix.join(&suffix);
412 assert_eq!(path.as_str(), "foo/bar/baz");
413
414 let prefix = Path::new("foo");
415 let suffix = Path::new("/bar/baz");
416 let path = prefix.join(&suffix);
417 assert_eq!(path.as_str(), "foo/bar/baz");
418
419 let prefix = Path::new("");
420 let suffix = Path::new("bar/baz");
421 let path = prefix.join(&suffix);
422 assert_eq!(path.as_str(), "bar/baz");
423 }
424
425 #[test]
426 fn test_path_prefix_conversions() {
427 let prefix1 = Path::from("foo/bar");
428 let prefix2 = Path::from(String::from("foo/bar"));
429 let s = String::from("foo/bar");
430 let prefix3 = Path::from(&s);
431
432 assert_eq!(prefix1.as_str(), "foo/bar");
433 assert_eq!(prefix2.as_str(), "foo/bar");
434 assert_eq!(prefix3.as_str(), "foo/bar");
435 }
436
437 #[test]
438 fn test_path_suffix_conversions() {
439 let suffix1 = Path::from("foo/bar");
440 let suffix2 = Path::from(String::from("foo/bar"));
441 let s = String::from("foo/bar");
442 let suffix3 = Path::from(&s);
443
444 assert_eq!(suffix1.as_str(), "foo/bar");
445 assert_eq!(suffix2.as_str(), "foo/bar");
446 assert_eq!(suffix3.as_str(), "foo/bar");
447 }
448
449 #[test]
450 fn test_path_types_basic_operations() {
451 let prefix = Path::new("foo/bar");
452 assert_eq!(prefix.as_str(), "foo/bar");
453 assert!(!prefix.is_empty());
454 assert_eq!(prefix.len(), 7);
455
456 let suffix = Path::new("baz/qux");
457 assert_eq!(suffix.as_str(), "baz/qux");
458 assert!(!suffix.is_empty());
459 assert_eq!(suffix.len(), 7);
460
461 let empty_prefix = Path::new("");
462 assert!(empty_prefix.is_empty());
463 assert_eq!(empty_prefix.len(), 0);
464
465 let empty_suffix = Path::new("");
466 assert!(empty_suffix.is_empty());
467 assert_eq!(empty_suffix.len(), 0);
468 }
469
470 #[test]
471 fn test_prefix_has_prefix() {
472 let prefix = Path::new("foo/bar");
474 assert!(prefix.has_prefix(""));
475
476 let prefix = Path::new("foo/bar");
478 assert!(prefix.has_prefix("foo/bar"));
479
480 assert!(prefix.has_prefix("foo"));
482 assert!(prefix.has_prefix("foo/"));
483
484 assert!(!prefix.has_prefix("f"));
486 assert!(!prefix.has_prefix("fo"));
487 assert!(!prefix.has_prefix("foo/b"));
488 assert!(!prefix.has_prefix("foo/ba"));
489
490 let prefix = Path::new("foobar");
492 assert!(!prefix.has_prefix("foo"));
493 assert!(prefix.has_prefix("foobar"));
494
495 let prefix = Path::new("foo/bar/");
497 assert!(prefix.has_prefix("foo"));
498 assert!(prefix.has_prefix("foo/"));
499 assert!(prefix.has_prefix("foo/bar"));
500 assert!(prefix.has_prefix("foo/bar/"));
501
502 let prefix = Path::new("foo");
504 assert!(prefix.has_prefix(""));
505 assert!(prefix.has_prefix("foo"));
506 assert!(prefix.has_prefix("foo/")); assert!(!prefix.has_prefix("f"));
508
509 let prefix = Path::new("");
511 assert!(prefix.has_prefix(""));
512 assert!(!prefix.has_prefix("foo"));
513 }
514
515 #[test]
516 fn test_prefix_join() {
517 let prefix = Path::new("foo");
519 let suffix = Path::new("bar");
520 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
521
522 let prefix = Path::new("foo/");
524 let suffix = Path::new("bar");
525 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
526
527 let prefix = Path::new("foo");
529 let suffix = Path::new("/bar");
530 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
531
532 let prefix = Path::new("foo");
534 let suffix = Path::new("bar/");
535 assert_eq!(prefix.join(suffix).as_str(), "foo/bar"); let prefix = Path::new("foo/");
539 let suffix = Path::new("/bar");
540 assert_eq!(prefix.join(suffix).as_str(), "foo/bar");
541
542 let prefix = Path::new("foo");
544 let suffix = Path::new("");
545 assert_eq!(prefix.join(suffix).as_str(), "foo");
546
547 let prefix = Path::new("");
549 let suffix = Path::new("bar");
550 assert_eq!(prefix.join(suffix).as_str(), "bar");
551
552 let prefix = Path::new("");
554 let suffix = Path::new("");
555 assert_eq!(prefix.join(suffix).as_str(), "");
556
557 let prefix = Path::new("foo/bar");
559 let suffix = Path::new("baz/qux");
560 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux");
561
562 let prefix = Path::new("foo/bar/");
564 let suffix = Path::new("/baz/qux/");
565 assert_eq!(prefix.join(suffix).as_str(), "foo/bar/baz/qux"); }
567
568 #[test]
569 fn test_path_ref() {
570 let ref1 = Path::new("/foo/bar/");
572 assert_eq!(ref1.as_str(), "foo/bar");
573
574 let ref2 = Path::from("///foo///");
575 assert_eq!(ref2.as_str(), "foo");
576
577 let ref3 = Path::new("foo//bar///baz");
579 assert_eq!(ref3.as_str(), "foo/bar/baz");
580
581 let path = Path::new("foo/bar");
583 let path_ref = path;
584 assert_eq!(path_ref.as_str(), "foo/bar");
585
586 let path2 = Path::new("foo/bar/baz");
588 assert!(path2.has_prefix(&path_ref));
589 assert_eq!(path2.strip_prefix(path_ref).unwrap().as_str(), "baz");
590
591 let empty = Path::new("");
593 assert!(empty.is_empty());
594 assert_eq!(empty.len(), 0);
595 }
596
597 #[test]
598 fn test_multiple_consecutive_slashes() {
599 let path = Path::new("foo//bar///baz");
600 assert_eq!(path.as_str(), "foo/bar/baz");
602
603 let path2 = Path::new("//foo//bar///baz//");
605 assert_eq!(path2.as_str(), "foo/bar/baz");
606
607 let path3 = Path::new("foo///bar");
609 assert_eq!(path3.as_str(), "foo/bar");
610 }
611
612 #[test]
613 fn test_removes_multiple_slashes_comprehensively() {
614 assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
616 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
617 assert_eq!(Path::new("foo////bar").as_str(), "foo/bar");
618
619 assert_eq!(Path::new("foo//bar//baz").as_str(), "foo/bar/baz");
621 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
622
623 assert_eq!(Path::new("foo//bar///baz////qux").as_str(), "foo/bar/baz/qux");
625
626 assert_eq!(Path::new("//foo//bar//").as_str(), "foo/bar");
628 assert_eq!(Path::new("///foo///bar///").as_str(), "foo/bar");
629
630 assert_eq!(Path::new("//").as_str(), "");
632 assert_eq!(Path::new("////").as_str(), "");
633
634 let path_with_slashes = Path::new("foo//bar///baz");
636 assert!(path_with_slashes.has_prefix("foo/bar"));
637 assert_eq!(path_with_slashes.strip_prefix("foo").unwrap().as_str(), "bar/baz");
638 assert_eq!(path_with_slashes.join("qux").as_str(), "foo/bar/baz/qux");
639
640 let path_ref = Path::new("foo//bar///baz");
642 assert_eq!(path_ref.as_str(), "foo/bar/baz"); let path_from_ref = path_ref.to_owned();
644 assert_eq!(path_from_ref.as_str(), "foo/bar/baz"); }
646
647 #[test]
648 fn test_path_ref_multiple_slashes() {
649 let path_ref = Path::new("//foo//bar///baz//");
651 assert_eq!(path_ref.as_str(), "foo/bar/baz"); assert_eq!(Path::new("foo//bar").as_str(), "foo/bar");
655 assert_eq!(Path::new("foo///bar").as_str(), "foo/bar");
656 assert_eq!(Path::new("a//b//c//d").as_str(), "a/b/c/d");
657
658 assert_eq!(Path::new("foo//bar").to_owned().as_str(), "foo/bar");
660 assert_eq!(Path::new("foo///bar").to_owned().as_str(), "foo/bar");
661 assert_eq!(Path::new("a//b//c//d").to_owned().as_str(), "a/b/c/d");
662
663 assert_eq!(Path::new("//").as_str(), "");
665 assert_eq!(Path::new("////").as_str(), "");
666 assert_eq!(Path::new("//").to_owned().as_str(), "");
667 assert_eq!(Path::new("////").to_owned().as_str(), "");
668
669 let normal_path = Path::new("foo/bar/baz");
671 assert_eq!(normal_path.as_str(), "foo/bar/baz");
672 let needs_norm = Path::new("foo//bar");
675 assert_eq!(needs_norm.as_str(), "foo/bar");
676 }
678
679 #[test]
680 fn test_ergonomic_conversions() {
681 fn takes_path_ref<'a>(p: impl Into<Path<'a>>) -> String {
683 p.into().as_str().to_string()
684 }
685
686 fn takes_path_ref_with_trait<'a>(p: impl Into<Path<'a>>) -> String {
688 p.into().as_str().to_string()
689 }
690
691 assert_eq!(takes_path_ref("foo//bar"), "foo/bar");
693
694 let owned_string = String::from("foo//bar///baz");
696 assert_eq!(takes_path_ref(owned_string), "foo/bar/baz");
697
698 let string_ref = String::from("foo//bar");
700 assert_eq!(takes_path_ref(string_ref), "foo/bar");
701
702 let path_ref = Path::new("foo//bar");
704 assert_eq!(takes_path_ref(path_ref), "foo/bar");
705
706 let path = Path::new("foo//bar");
708 assert_eq!(takes_path_ref(path), "foo/bar");
709
710 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");
718 assert_eq!(takes_path_ref_with_trait(String::from("foo//bar")), "foo/bar");
719 }
720
721 #[test]
722 fn test_prefix_strip_prefix() {
723 let prefix = Path::new("foo/bar/baz");
725 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
726 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar/baz");
727 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar/baz");
728 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "baz");
729 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "baz");
730 assert_eq!(prefix.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
731
732 assert!(prefix.strip_prefix("fo").is_none());
734 assert!(prefix.strip_prefix("bar").is_none());
735 assert!(prefix.strip_prefix("foo/ba").is_none());
736
737 let prefix = Path::new("foobar");
739 assert!(prefix.strip_prefix("foo").is_none());
740 assert_eq!(prefix.strip_prefix("foobar").unwrap().as_str(), "");
741
742 let prefix = Path::new("");
744 assert_eq!(prefix.strip_prefix("").unwrap().as_str(), "");
745 assert!(prefix.strip_prefix("foo").is_none());
746
747 let prefix = Path::new("foo");
749 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "");
750 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), ""); let prefix = Path::new("foo/bar/");
754 assert_eq!(prefix.strip_prefix("foo").unwrap().as_str(), "bar");
755 assert_eq!(prefix.strip_prefix("foo/").unwrap().as_str(), "bar");
756 assert_eq!(prefix.strip_prefix("foo/bar").unwrap().as_str(), "");
757 assert_eq!(prefix.strip_prefix("foo/bar/").unwrap().as_str(), "");
758 }
759}