1use std::fmt::{self, Display};
2
3use crate::coding::{Decode, DecodeError, Encode};
4
5#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
10pub struct PathRef<'a>(&'a str);
11
12impl<'a> PathRef<'a> {
13 pub fn new(s: &'a str) -> Self {
17 Self(s.trim_start_matches('/').trim_end_matches('/'))
18 }
19
20 pub fn as_str(&self) -> &str {
22 self.0
23 }
24
25 pub fn is_empty(&self) -> bool {
27 self.0.is_empty()
28 }
29
30 pub fn len(&self) -> usize {
32 self.0.len()
33 }
34
35 pub fn to_path(&self) -> Path {
37 Path::new(self.0)
38 }
39}
40
41impl<'a> From<&'a str> for PathRef<'a> {
42 fn from(s: &'a str) -> Self {
43 Self::new(s)
44 }
45}
46
47impl<'a> From<&'a String> for PathRef<'a> {
48 fn from(s: &'a String) -> Self {
49 Self::new(s.as_str())
50 }
51}
52
53impl<'a> From<&'a Path> for PathRef<'a> {
54 fn from(p: &'a Path) -> Self {
55 Self(p.0.as_str())
57 }
58}
59
60impl<'a, 'b> From<&'a PathRef<'b>> for PathRef<'a>
61where
62 'b: 'a,
63{
64 fn from(p: &'a PathRef<'b>) -> Self {
65 Self(p.0)
66 }
67}
68
69impl<'a> AsRef<str> for PathRef<'a> {
70 fn as_ref(&self) -> &str {
71 self.0
72 }
73}
74
75impl<'a> Display for PathRef<'a> {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 write!(f, "{}", self.0)
78 }
79}
80
81#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
107#[cfg_attr(feature = "serde", derive(serde::Serialize))]
108pub struct Path(String);
109
110impl Path {
111 pub fn new<S: Into<String>>(s: S) -> Self {
115 let s = s.into();
116 Self(s.trim_start_matches('/').trim_end_matches('/').to_string())
117 }
118
119 pub fn has_prefix<'a>(&self, prefix: impl Into<PathRef<'a>>) -> bool {
141 let prefix = prefix.into();
142 if prefix.is_empty() {
143 return true;
144 }
145
146 if !self.0.starts_with(prefix.as_str()) {
147 return false;
148 }
149
150 if self.0.len() == prefix.len() {
152 return true;
153 }
154
155 self.0.chars().nth(prefix.len()) == Some('/')
157 }
158
159 pub fn strip_prefix<'a>(&'a self, prefix: impl Into<PathRef<'a>>) -> Option<PathRef<'a>> {
175 let prefix = prefix.into();
176 if !self.has_prefix(prefix) {
177 return None;
178 }
179
180 let suffix = &self.0[prefix.len()..];
181 let suffix = suffix.trim_start_matches('/');
183 Some(PathRef(suffix))
184 }
185
186 pub fn as_str(&self) -> &str {
188 &self.0
189 }
190
191 pub fn is_empty(&self) -> bool {
193 self.0.is_empty()
194 }
195
196 pub fn len(&self) -> usize {
198 self.0.len()
199 }
200
201 pub fn join<'a>(&self, other: impl Into<PathRef<'a>>) -> Path {
215 let other = other.into();
216 if self.0.is_empty() {
217 other.to_path()
218 } else if other.is_empty() {
219 self.clone()
220 } else {
221 Path::new(format!("{}/{}", self.0, other.as_str()))
223 }
224 }
225}
226
227impl Display for Path {
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 write!(f, "{}", self.0)
230 }
231}
232
233impl AsRef<str> for Path {
234 fn as_ref(&self) -> &str {
235 &self.0
236 }
237}
238
239impl From<String> for Path {
240 fn from(s: String) -> Self {
241 Self::new(s)
242 }
243}
244
245impl From<&str> for Path {
246 fn from(s: &str) -> Self {
247 Self::new(s)
248 }
249}
250
251impl From<&String> for Path {
252 fn from(s: &String) -> Self {
253 Self::new(s)
254 }
255}
256
257impl From<&Path> for Path {
258 fn from(p: &Path) -> Self {
259 p.clone()
260 }
261}
262
263impl From<PathRef<'_>> for Path {
264 fn from(p: PathRef<'_>) -> Self {
265 Path::new(p.0)
266 }
267}
268
269impl Decode for Path {
270 fn decode<R: bytes::Buf>(r: &mut R) -> Result<Self, DecodeError> {
271 let path = String::decode(r)?;
272 Ok(Self::new(path))
273 }
274}
275
276impl Encode for Path {
277 fn encode<W: bytes::BufMut>(&self, w: &mut W) {
278 self.0.encode(w)
279 }
280}
281
282#[cfg(feature = "serde")]
283impl<'de> serde::Deserialize<'de> for Path {
284 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
285 where
286 D: serde::Deserializer<'de>,
287 {
288 let s = String::deserialize(deserializer)?;
289 Ok(Path::new(s))
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296
297 #[test]
298 fn test_has_prefix() {
299 let path = Path::new("foo/bar/baz");
300
301 assert!(path.has_prefix(""));
303 assert!(path.has_prefix("foo"));
304 assert!(path.has_prefix(&Path::new("foo")));
305 assert!(path.has_prefix("foo/"));
306 assert!(path.has_prefix("foo/bar"));
307 assert!(path.has_prefix(&Path::new("foo/bar/")));
308 assert!(path.has_prefix("foo/bar/baz"));
309
310 assert!(!path.has_prefix("f"));
312 assert!(!path.has_prefix(&Path::new("fo")));
313 assert!(!path.has_prefix("foo/b"));
314 assert!(!path.has_prefix("foo/ba"));
315 assert!(!path.has_prefix(&Path::new("foo/bar/ba")));
316
317 let path = Path::new("foobar");
319 assert!(!path.has_prefix("foo"));
320 assert!(path.has_prefix(&Path::new("foobar")));
321 }
322
323 #[test]
324 fn test_strip_prefix() {
325 let path = Path::new("foo/bar/baz");
326
327 assert_eq!(path.strip_prefix("").unwrap().as_str(), "foo/bar/baz");
329 assert_eq!(path.strip_prefix("foo").unwrap().as_str(), "bar/baz");
330 assert_eq!(path.strip_prefix(&Path::new("foo/")).unwrap().as_str(), "bar/baz");
331 assert_eq!(path.strip_prefix("foo/bar").unwrap().as_str(), "baz");
332 assert_eq!(path.strip_prefix(&Path::new("foo/bar/")).unwrap().as_str(), "baz");
333 assert_eq!(path.strip_prefix("foo/bar/baz").unwrap().as_str(), "");
334
335 assert!(path.strip_prefix("fo").is_none());
337 assert!(path.strip_prefix(&Path::new("bar")).is_none());
338 }
339
340 #[test]
341 fn test_join() {
342 assert_eq!(Path::new("foo").join("bar").as_str(), "foo/bar");
344 assert_eq!(Path::new("foo/").join(&Path::new("bar")).as_str(), "foo/bar");
345 assert_eq!(Path::new("").join("bar").as_str(), "bar");
346 assert_eq!(Path::new("foo/bar").join(&Path::new("baz")).as_str(), "foo/bar/baz");
347 }
348
349 #[test]
350 fn test_empty() {
351 let empty = Path::new("");
352 assert!(empty.is_empty());
353 assert_eq!(empty.len(), 0);
354
355 let non_empty = Path::new("foo");
356 assert!(!non_empty.is_empty());
357 assert_eq!(non_empty.len(), 3);
358 }
359
360 #[test]
361 fn test_from_conversions() {
362 let path1 = Path::from("foo/bar");
363 let path2 = Path::from(String::from("foo/bar"));
364 let s = String::from("foo/bar");
365 let path3 = Path::from(&s);
366
367 assert_eq!(path1.as_str(), "foo/bar");
368 assert_eq!(path2.as_str(), "foo/bar");
369 assert_eq!(path3.as_str(), "foo/bar");
370 }
371
372 #[test]
373 fn test_path_prefix_join() {
374 let prefix = Path::new("foo");
375 let suffix = Path::new("bar/baz");
376 let path = prefix.join(&suffix);
377 assert_eq!(path.as_str(), "foo/bar/baz");
378
379 let prefix = Path::new("foo/");
380 let suffix = Path::new("bar/baz");
381 let path = prefix.join(&suffix);
382 assert_eq!(path.as_str(), "foo/bar/baz");
383
384 let prefix = Path::new("foo");
385 let suffix = Path::new("/bar/baz");
386 let path = prefix.join(&suffix);
387 assert_eq!(path.as_str(), "foo/bar/baz");
388
389 let prefix = Path::new("");
390 let suffix = Path::new("bar/baz");
391 let path = prefix.join(&suffix);
392 assert_eq!(path.as_str(), "bar/baz");
393 }
394
395 #[test]
396 fn test_path_prefix_conversions() {
397 let prefix1 = Path::from("foo/bar");
398 let prefix2 = Path::from(String::from("foo/bar"));
399 let s = String::from("foo/bar");
400 let prefix3 = Path::from(&s);
401
402 assert_eq!(prefix1.as_str(), "foo/bar");
403 assert_eq!(prefix2.as_str(), "foo/bar");
404 assert_eq!(prefix3.as_str(), "foo/bar");
405 }
406
407 #[test]
408 fn test_path_suffix_conversions() {
409 let suffix1 = Path::from("foo/bar");
410 let suffix2 = Path::from(String::from("foo/bar"));
411 let s = String::from("foo/bar");
412 let suffix3 = Path::from(&s);
413
414 assert_eq!(suffix1.as_str(), "foo/bar");
415 assert_eq!(suffix2.as_str(), "foo/bar");
416 assert_eq!(suffix3.as_str(), "foo/bar");
417 }
418
419 #[test]
420 fn test_path_types_basic_operations() {
421 let prefix = Path::new("foo/bar");
422 assert_eq!(prefix.as_str(), "foo/bar");
423 assert!(!prefix.is_empty());
424 assert_eq!(prefix.len(), 7);
425
426 let suffix = Path::new("baz/qux");
427 assert_eq!(suffix.as_str(), "baz/qux");
428 assert!(!suffix.is_empty());
429 assert_eq!(suffix.len(), 7);
430
431 let empty_prefix = Path::new("");
432 assert!(empty_prefix.is_empty());
433 assert_eq!(empty_prefix.len(), 0);
434
435 let empty_suffix = Path::new("");
436 assert!(empty_suffix.is_empty());
437 assert_eq!(empty_suffix.len(), 0);
438 }
439
440 #[test]
441 fn test_prefix_has_prefix() {
442 let prefix = Path::new("foo/bar");
444 assert!(prefix.has_prefix(&Path::new("")));
445
446 let prefix = Path::new("foo/bar");
448 assert!(prefix.has_prefix(&Path::new("foo/bar")));
449
450 assert!(prefix.has_prefix(&Path::new("foo")));
452 assert!(prefix.has_prefix(&Path::new("foo/")));
453
454 assert!(!prefix.has_prefix(&Path::new("f")));
456 assert!(!prefix.has_prefix(&Path::new("fo")));
457 assert!(!prefix.has_prefix(&Path::new("foo/b")));
458 assert!(!prefix.has_prefix(&Path::new("foo/ba")));
459
460 let prefix = Path::new("foobar");
462 assert!(!prefix.has_prefix(&Path::new("foo")));
463 assert!(prefix.has_prefix(&Path::new("foobar")));
464
465 let prefix = Path::new("foo/bar/");
467 assert!(prefix.has_prefix(&Path::new("foo")));
468 assert!(prefix.has_prefix(&Path::new("foo/")));
469 assert!(prefix.has_prefix(&Path::new("foo/bar")));
470 assert!(prefix.has_prefix(&Path::new("foo/bar/")));
471
472 let prefix = Path::new("foo");
474 assert!(prefix.has_prefix(&Path::new("")));
475 assert!(prefix.has_prefix(&Path::new("foo")));
476 assert!(prefix.has_prefix(&Path::new("foo/"))); assert!(!prefix.has_prefix(&Path::new("f")));
478
479 let prefix = Path::new("");
481 assert!(prefix.has_prefix(&Path::new("")));
482 assert!(!prefix.has_prefix(&Path::new("foo")));
483 }
484
485 #[test]
486 fn test_prefix_join() {
487 let prefix = Path::new("foo");
489 let suffix = Path::new("bar");
490 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar");
491
492 let prefix = Path::new("foo/");
494 let suffix = Path::new("bar");
495 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar");
496
497 let prefix = Path::new("foo");
499 let suffix = Path::new("/bar");
500 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar");
501
502 let prefix = Path::new("foo");
504 let suffix = Path::new("bar/");
505 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar"); let prefix = Path::new("foo/");
509 let suffix = Path::new("/bar");
510 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar");
511
512 let prefix = Path::new("foo");
514 let suffix = Path::new("");
515 assert_eq!(prefix.join(&suffix).as_str(), "foo");
516
517 let prefix = Path::new("");
519 let suffix = Path::new("bar");
520 assert_eq!(prefix.join(&suffix).as_str(), "bar");
521
522 let prefix = Path::new("");
524 let suffix = Path::new("");
525 assert_eq!(prefix.join(&suffix).as_str(), "");
526
527 let prefix = Path::new("foo/bar");
529 let suffix = Path::new("baz/qux");
530 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar/baz/qux");
531
532 let prefix = Path::new("foo/bar/");
534 let suffix = Path::new("/baz/qux/");
535 assert_eq!(prefix.join(&suffix).as_str(), "foo/bar/baz/qux"); }
537
538 #[test]
539 fn test_path_ref() {
540 let ref1 = PathRef::new("/foo/bar/");
542 assert_eq!(ref1.as_str(), "foo/bar");
543
544 let ref2 = PathRef::from("///foo///");
545 assert_eq!(ref2.as_str(), "foo");
546
547 let path = Path::new("foo/bar");
549 let path_ref = PathRef::from(&path);
550 assert_eq!(path_ref.as_str(), "foo/bar");
551
552 let path2 = Path::new("foo/bar/baz");
554 assert!(path2.has_prefix(path_ref));
555 assert_eq!(path2.strip_prefix(path_ref).unwrap().as_str(), "baz");
556
557 let empty = PathRef::new("");
559 assert!(empty.is_empty());
560 assert_eq!(empty.len(), 0);
561 }
562
563 #[test]
564 fn test_prefix_strip_prefix() {
565 let prefix = Path::new("foo/bar/baz");
567 assert_eq!(prefix.strip_prefix(&Path::new("")).unwrap().as_str(), "foo/bar/baz");
568 assert_eq!(prefix.strip_prefix(&Path::new("foo")).unwrap().as_str(), "bar/baz");
569 assert_eq!(prefix.strip_prefix(&Path::new("foo/")).unwrap().as_str(), "bar/baz");
570 assert_eq!(prefix.strip_prefix(&Path::new("foo/bar")).unwrap().as_str(), "baz");
571 assert_eq!(prefix.strip_prefix(&Path::new("foo/bar/")).unwrap().as_str(), "baz");
572 assert_eq!(prefix.strip_prefix(&Path::new("foo/bar/baz")).unwrap().as_str(), "");
573
574 assert!(prefix.strip_prefix(&Path::new("fo")).is_none());
576 assert!(prefix.strip_prefix(&Path::new("bar")).is_none());
577 assert!(prefix.strip_prefix(&Path::new("foo/ba")).is_none());
578
579 let prefix = Path::new("foobar");
581 assert!(prefix.strip_prefix(&Path::new("foo")).is_none());
582 assert_eq!(prefix.strip_prefix(&Path::new("foobar")).unwrap().as_str(), "");
583
584 let prefix = Path::new("");
586 assert_eq!(prefix.strip_prefix(&Path::new("")).unwrap().as_str(), "");
587 assert!(prefix.strip_prefix(&Path::new("foo")).is_none());
588
589 let prefix = Path::new("foo");
591 assert_eq!(prefix.strip_prefix(&Path::new("foo")).unwrap().as_str(), "");
592 assert_eq!(prefix.strip_prefix(&Path::new("foo/")).unwrap().as_str(), ""); let prefix = Path::new("foo/bar/");
596 assert_eq!(prefix.strip_prefix(&Path::new("foo")).unwrap().as_str(), "bar");
597 assert_eq!(prefix.strip_prefix(&Path::new("foo/")).unwrap().as_str(), "bar");
598 assert_eq!(prefix.strip_prefix(&Path::new("foo/bar")).unwrap().as_str(), "");
599 assert_eq!(prefix.strip_prefix(&Path::new("foo/bar/")).unwrap().as_str(), "");
600 }
601}