standard_lib/fs/path/
dispatch.rs

1use std::{ffi::OsStr, num::NonZero, str::FromStr};
2
3use crate::fs::error::{EmptyStrError, HomeResolutionError};
4
5use super::{Abs, OwnedPath, Path, PathParseError, Rel};
6
7/// An OwnedPath with a statically dispatched state. TODO
8// TODO: More docs here.
9pub enum DispatchedPath {
10    Abs(OwnedPath<Abs>),
11    Rel(OwnedPath<Rel>),
12}
13
14impl FromStr for DispatchedPath {
15    // TODO: Is it fair to sanitize in a parse implementation?
16    type Err = PathParseError;
17
18    fn from_str(s: &str) -> Result<Self, Self::Err> {
19        match s.chars().next() {
20            Some('/') => {
21                Ok(DispatchedPath::Abs(
22                    OwnedPath::from(s)
23                ))
24            },
25            // TODO: Handle '~' more consistently.
26            Some('~') => {
27                Ok(DispatchedPath::Abs(
28                    OwnedPath::from(&s[1..])
29                        .resolve_home()
30                        .ok_or(HomeResolutionError)?
31                ))
32            },
33            // '.' is caught here, so "./" matches and is then sanitized to a relative "/".
34            Some(_) => {
35                Ok(DispatchedPath::Rel(
36                    OwnedPath::from(s)
37                ))
38            },
39            None => Err(EmptyStrError)?,
40        }
41    }
42}
43
44macro_rules! dispatch_ref {
45    ($this:ident.$name:ident) => {
46        match $this {
47            DispatchedPath::Abs(abs) => abs.$name(),
48            DispatchedPath::Rel(rel) => rel.$name(),
49        }
50    };
51}
52
53impl DispatchedPath {
54    pub fn abs_or_resolve(self, target: OwnedPath<Abs>) -> OwnedPath<Abs> {
55        match self {
56            DispatchedPath::Abs(abs) => abs,
57            DispatchedPath::Rel(rel) => rel.resolve(target),
58        }
59    }
60
61    pub fn rel_or_make_relative<P: AsRef<Path<Abs>>>(self, target: P) -> OwnedPath<Rel> {
62        match self {
63            DispatchedPath::Abs(abs) => abs.make_relative(target),
64            DispatchedPath::Rel(rel) => rel,
65        }
66    }
67
68    pub fn len(&self) -> NonZero<usize> {
69        match self {
    DispatchedPath::Abs(abs) => abs.len(),
    DispatchedPath::Rel(rel) => rel.len(),
}dispatch_ref!(self.len)
70    }
71
72    pub fn as_os_str(&self) -> &OsStr {
73        match self {
    DispatchedPath::Abs(abs) => abs.as_os_str(),
    DispatchedPath::Rel(rel) => rel.as_os_str(),
}dispatch_ref!(self.as_os_str)
74    }
75
76    pub fn as_os_str_no_lead(&self) -> &OsStr {
77        match self {
    DispatchedPath::Abs(abs) => abs.as_os_str_no_lead(),
    DispatchedPath::Rel(rel) => rel.as_os_str_no_lead(),
}dispatch_ref!(self.as_os_str_no_lead)
78    }
79
80    pub fn as_bytes(&self) -> &[u8] {
81        match self {
    DispatchedPath::Abs(abs) => abs.as_bytes(),
    DispatchedPath::Rel(rel) => rel.as_bytes(),
}dispatch_ref!(self.as_bytes)
82    }
83
84    pub fn basename(&self) -> &OsStr {
85        match self {
    DispatchedPath::Abs(abs) => abs.basename(),
    DispatchedPath::Rel(rel) => rel.basename(),
}dispatch_ref!(self.basename)
86    }
87}
88
89impl From<OwnedPath<Abs>> for DispatchedPath {
90    fn from(value: OwnedPath<Abs>) -> Self {
91        DispatchedPath::Abs(value)
92    }
93}
94
95impl From<OwnedPath<Rel>> for DispatchedPath {
96    fn from(value: OwnedPath<Rel>) -> Self {
97        DispatchedPath::Rel(value)
98    }
99}
100
101// TODO: Forwarding to generic OwnedPath and Path methods.