pathlib/
comp.rs

1use crate::ParsablePath;
2use core::marker::PhantomData;
3
4/// A path component.
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
6pub enum Component<'a> {
7    /// A path prefix.
8    Prefix(&'a str),
9    /// A root component.
10    Root,
11    /// A current directory component.
12    CurDir,
13    /// A parent directory component.
14    ParentDir,
15    /// A normal component.
16    Normal(&'a str),
17}
18
19impl Component<'_> {
20    /// Returns the component as a string.
21    pub fn as_str(&self) -> &str {
22        match self {
23            Component::Prefix(s) => s,
24            Component::Root => "/",
25            Component::CurDir => ".",
26            Component::ParentDir => "..",
27            Component::Normal(s) => s,
28        }
29    }
30}
31
32#[cfg(feature = "std")]
33mod std_impls {
34    use super::*;
35    use std::path::Component as StdComponent;
36
37    impl<'a> TryFrom<StdComponent<'a>> for Component<'a> {
38        type Error = ();
39
40        fn try_from(c: StdComponent<'a>) -> Result<Self, Self::Error> {
41            match c {
42                StdComponent::Prefix(p) => Ok(Component::Prefix(p.as_os_str().to_str().ok_or(())?)),
43                StdComponent::RootDir => Ok(Component::Root),
44                StdComponent::CurDir => Ok(Component::CurDir),
45                StdComponent::ParentDir => Ok(Component::ParentDir),
46                StdComponent::Normal(p) => Ok(Component::Normal(p.to_str().ok_or(())?)),
47            }
48        }
49    }
50
51    impl PartialEq<StdComponent<'_>> for Component<'_> {
52        fn eq(&self, other: &StdComponent<'_>) -> bool {
53            match (self, other) {
54                (Component::Prefix(a), StdComponent::Prefix(b)) => {
55                    Some(*a) == b.as_os_str().to_str()
56                }
57                (Component::Root, StdComponent::RootDir) => true,
58                (Component::CurDir, StdComponent::CurDir) => true,
59                (Component::ParentDir, StdComponent::ParentDir) => true,
60                (Component::Normal(a), StdComponent::Normal(b)) => a == b,
61                _ => false,
62            }
63        }
64    }
65
66    impl<'a> PartialEq<Component<'a>> for StdComponent<'a> {
67        fn eq(&self, other: &Component<'a>) -> bool {
68            other == self
69        }
70    }
71}
72
73/// An iterator over the [Component]s of a path.
74pub struct Components<'a, P: ParsablePath> {
75    s: &'a str,
76    p: PhantomData<P>,
77    progressed_front: bool,
78    progressed_back: bool,
79}
80
81impl<'a, P: ParsablePath> Components<'a, P> {
82    /// Creates a new [Components] iterator.
83    pub fn new(s: &'a str) -> Self {
84        Self {
85            s,
86            p: PhantomData,
87            progressed_front: false,
88            progressed_back: false,
89        }
90    }
91}
92
93impl<'a, P: ParsablePath> Iterator for Components<'a, P> {
94    type Item = Component<'a>;
95
96    fn next(&mut self) -> Option<Self::Item> {
97        let (first, rest) = P::split_first_component(self.s, self.progressed_front);
98        self.progressed_front = true;
99        self.s = rest.unwrap_or("");
100        first
101    }
102}
103
104impl<P: ParsablePath> DoubleEndedIterator for Components<'_, P> {
105    fn next_back(&mut self) -> Option<Self::Item> {
106        let (rest, last) = P::split_last_component(self.s, self.progressed_back);
107        self.progressed_back = true;
108        self.s = rest.unwrap_or("");
109        last
110    }
111}