Skip to main content

typed_path/windows/utf8/
components.rs

1mod component;
2
3use core::{cmp, fmt, iter};
4
5pub use component::*;
6
7use crate::windows::WindowsComponents;
8use crate::{private, Components, Utf8Components, Utf8Encoding, Utf8Path};
9
10/// Represents a Windows-specific [`Components`]
11#[derive(Clone)]
12pub struct Utf8WindowsComponents<'a> {
13    inner: WindowsComponents<'a>,
14}
15
16impl<'a> Utf8WindowsComponents<'a> {
17    pub(crate) fn new(path: &'a str) -> Self {
18        Self {
19            inner: WindowsComponents::new(path.as_bytes()),
20        }
21    }
22
23    /// Extracts a slice corresponding to the portion of the path remaining for iteration.
24    ///
25    /// # Examples
26    ///
27    /// ```
28    /// use typed_path::{Utf8Path, Utf8WindowsEncoding};
29    ///
30    /// // NOTE: A path cannot be created on its own without a defined encoding
31    /// let mut components = Utf8Path::<Utf8WindowsEncoding>::new(r"\tmp\foo\bar.txt").components();
32    /// components.next();
33    /// components.next();
34    ///
35    /// assert_eq!(Utf8Path::<Utf8WindowsEncoding>::new(r"foo\bar.txt"), components.as_path());
36    /// ```
37    pub fn as_path<T>(&self) -> &'a Utf8Path<T>
38    where
39        T: Utf8Encoding,
40    {
41        Utf8Path::new(self.as_str())
42    }
43}
44
45impl private::Sealed for Utf8WindowsComponents<'_> {}
46
47impl<'a> Utf8Components<'a> for Utf8WindowsComponents<'a> {
48    type Component = Utf8WindowsComponent<'a>;
49
50    fn as_str(&self) -> &'a str {
51        // NOTE: We know that the internal byte representation is UTF-8 compliant as we ensure that
52        //       the only input provided is UTF-8 and no modifications are made with non-UTF-8 bytes
53        unsafe { core::str::from_utf8_unchecked(self.inner.as_bytes()) }
54    }
55
56    /// Returns true only if the path represented by the components
57    /// has a prefix followed by a root directory
58    ///
59    /// e.g. `C:\some\path` -> true, `C:some\path` -> false
60    fn is_absolute(&self) -> bool {
61        self.inner.is_absolute()
62    }
63
64    /// Returns true if the `path` has either:
65    ///
66    /// * physical root, meaning it begins with the separator (e.g. `\my\path` or `C:\`)
67    /// * implicit root, meaning it begins with a prefix that is not a drive (e.g. `\\?\pictures`)
68    fn has_root(&self) -> bool {
69        self.inner.has_root()
70    }
71}
72
73impl<'a> Utf8WindowsComponents<'a> {
74    fn peek_front(&self) -> Option<<Self as Utf8Components<'a>>::Component> {
75        self.clone().next()
76    }
77
78    /// Returns true if the represented path has a prefix
79    #[inline]
80    pub fn has_prefix(&self) -> bool {
81        self.prefix().is_some()
82    }
83
84    /// Returns the prefix of the represented path's components if it has one
85    pub fn prefix(&self) -> Option<Utf8WindowsPrefixComponent<'_>> {
86        match self.peek_front() {
87            Some(Utf8WindowsComponent::Prefix(p)) => Some(p),
88            _ => None,
89        }
90    }
91
92    /// Returns the kind of prefix associated with the represented path if it has one
93    #[inline]
94    pub fn prefix_kind(&self) -> Option<Utf8WindowsPrefix<'_>> {
95        self.prefix().map(|p| p.kind())
96    }
97
98    /// Returns true if represented path has a verbatim, verbatim UNC, or verbatim disk prefix
99    pub fn has_any_verbatim_prefix(&self) -> bool {
100        matches!(
101            self.prefix_kind(),
102            Some(
103                Utf8WindowsPrefix::Verbatim(_)
104                    | Utf8WindowsPrefix::UNC(..)
105                    | Utf8WindowsPrefix::Disk(_)
106            )
107        )
108    }
109
110    /// Returns true if represented path has a verbatim prefix (e.g. `\\?\pictures)
111    pub fn has_verbatim_prefix(&self) -> bool {
112        matches!(self.prefix_kind(), Some(Utf8WindowsPrefix::Verbatim(_)))
113    }
114
115    /// Returns true if represented path has a verbatim UNC prefix (e.g. `\\?\UNC\server\share`)
116    pub fn has_verbatim_unc_prefix(&self) -> bool {
117        matches!(self.prefix_kind(), Some(Utf8WindowsPrefix::VerbatimUNC(..)))
118    }
119
120    /// Returns true if represented path has a verbatim disk prefix (e.g. `\\?\C:`)
121    pub fn has_verbatim_disk_prefix(&self) -> bool {
122        matches!(self.prefix_kind(), Some(Utf8WindowsPrefix::VerbatimDisk(_)))
123    }
124
125    /// Returns true if represented path has a device NS prefix (e.g. `\\.\BrainInterface`)
126    pub fn has_device_ns_prefix(&self) -> bool {
127        matches!(self.prefix_kind(), Some(Utf8WindowsPrefix::DeviceNS(_)))
128    }
129
130    /// Returns true if represented path has a UNC prefix (e.g. `\\server\share`)
131    pub fn has_unc_prefix(&self) -> bool {
132        matches!(self.prefix_kind(), Some(Utf8WindowsPrefix::UNC(..)))
133    }
134
135    /// Returns true if represented path has a disk prefix (e.g. `C:`)
136    pub fn has_disk_prefix(&self) -> bool {
137        matches!(self.prefix_kind(), Some(Utf8WindowsPrefix::Disk(_)))
138    }
139
140    /// Returns true if there is a separator immediately after the prefix, or separator
141    /// starts the components if there is no prefix
142    ///
143    /// e.g. `C:\` and `\path` would return true whereas `\\?\path` would return false
144    pub fn has_physical_root(&self) -> bool {
145        self.inner.has_physical_root()
146    }
147
148    /// Returns true if there is a root separator without a [`Utf8WindowsComponent::RootDir`]
149    /// needing to be present. This is tied to prefixes like verbatim `\\?\` and UNC `\\`.
150    ///
151    /// Really, it's everything but a disk prefix of `C:` that provide an implicit root
152    pub fn has_implicit_root(&self) -> bool {
153        match self.prefix().map(|p| p.kind()) {
154            Some(Utf8WindowsPrefix::Disk(_)) | None => false,
155            Some(_) => true,
156        }
157    }
158}
159
160impl AsRef<[u8]> for Utf8WindowsComponents<'_> {
161    #[inline]
162    fn as_ref(&self) -> &[u8] {
163        self.as_str().as_bytes()
164    }
165}
166
167impl AsRef<str> for Utf8WindowsComponents<'_> {
168    #[inline]
169    fn as_ref(&self) -> &str {
170        self.as_str()
171    }
172}
173
174impl<T> AsRef<Utf8Path<T>> for Utf8WindowsComponents<'_>
175where
176    T: Utf8Encoding,
177{
178    #[inline]
179    fn as_ref(&self) -> &Utf8Path<T> {
180        Utf8Path::new(self.as_str())
181    }
182}
183
184impl fmt::Debug for Utf8WindowsComponents<'_> {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        struct DebugHelper<'a>(Utf8WindowsComponents<'a>);
187
188        impl fmt::Debug for DebugHelper<'_> {
189            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190                f.debug_list().entries(self.0.clone()).finish()
191            }
192        }
193
194        f.debug_tuple("Utf8WindowsComponents")
195            .field(&DebugHelper(self.clone()))
196            .finish()
197    }
198}
199
200impl<'a> Iterator for Utf8WindowsComponents<'a> {
201    type Item = <Self as Utf8Components<'a>>::Component;
202
203    fn next(&mut self) -> Option<Self::Item> {
204        self.inner
205            .next()
206            .map(|c| unsafe { Utf8WindowsComponent::from_utf8_unchecked(&c) })
207    }
208}
209
210impl DoubleEndedIterator for Utf8WindowsComponents<'_> {
211    fn next_back(&mut self) -> Option<Self::Item> {
212        self.inner
213            .next_back()
214            .map(|c| unsafe { Utf8WindowsComponent::from_utf8_unchecked(&c) })
215    }
216}
217
218impl iter::FusedIterator for Utf8WindowsComponents<'_> {}
219
220impl cmp::PartialEq for Utf8WindowsComponents<'_> {
221    #[inline]
222    fn eq(&self, other: &Self) -> bool {
223        PartialEq::eq(&self.inner, &other.inner)
224    }
225}
226
227impl cmp::Eq for Utf8WindowsComponents<'_> {}
228
229impl cmp::PartialOrd for Utf8WindowsComponents<'_> {
230    #[inline]
231    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
232        Some(self.cmp(other))
233    }
234}
235
236impl cmp::Ord for Utf8WindowsComponents<'_> {
237    #[inline]
238    fn cmp(&self, other: &Self) -> cmp::Ordering {
239        Ord::cmp(&self.inner, &other.inner)
240    }
241}