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, or a UNC prefix
58    /// alone.
59    ///
60    /// A disk prefix alone is not absolute, because Windows treats
61    /// such a path (e.g. `C:file.txt`) as relative to a per-disk
62    /// current working directory, as a historical relic of MS-DOS.
63    /// However, a complete UNC prefix (e.g. `\\server\share`) is
64    /// absolute, because it is equivalent to the same path with a
65    /// trailing `\` when pushing path components.
66    ///
67    /// e.g. `C:\some\path` -> true, `C:some\path` -> false,
68    ///      `\\some.server\share` -> true, `\\some.server` -> false
69    fn is_absolute(&self) -> bool {
70        self.inner.is_absolute()
71    }
72
73    /// Returns true if the `path` has either:
74    ///
75    /// * physical root, meaning it begins with the separator (e.g. `\my\path` or `C:\`)
76    /// * implicit root, meaning it begins with a prefix that is not a drive (e.g. `\\?\pictures`)
77    fn has_root(&self) -> bool {
78        self.inner.has_root()
79    }
80}
81
82impl<'a> Utf8WindowsComponents<'a> {
83    fn peek_front(&self) -> Option<<Self as Utf8Components<'a>>::Component> {
84        self.clone().next()
85    }
86
87    /// Returns true if the represented path has a prefix
88    #[inline]
89    pub fn has_prefix(&self) -> bool {
90        self.prefix().is_some()
91    }
92
93    /// Returns the prefix of the represented path's components if it has one
94    pub fn prefix(&self) -> Option<Utf8WindowsPrefixComponent<'_>> {
95        match self.peek_front() {
96            Some(Utf8WindowsComponent::Prefix(p)) => Some(p),
97            _ => None,
98        }
99    }
100
101    /// Returns the kind of prefix associated with the represented path if it has one
102    #[inline]
103    pub fn prefix_kind(&self) -> Option<Utf8WindowsPrefix<'_>> {
104        self.prefix().map(|p| p.kind())
105    }
106
107    /// Returns true if represented path has a verbatim, verbatim UNC, or verbatim disk prefix
108    pub fn has_any_verbatim_prefix(&self) -> bool {
109        matches!(
110            self.prefix_kind(),
111            Some(
112                Utf8WindowsPrefix::Verbatim(_)
113                    | Utf8WindowsPrefix::UNC(..)
114                    | Utf8WindowsPrefix::Disk(_)
115            )
116        )
117    }
118
119    /// Returns true if represented path has a verbatim prefix (e.g. `\\?\pictures)
120    pub fn has_verbatim_prefix(&self) -> bool {
121        matches!(self.prefix_kind(), Some(Utf8WindowsPrefix::Verbatim(_)))
122    }
123
124    /// Returns true if represented path has a verbatim UNC prefix (e.g. `\\?\UNC\server\share`)
125    pub fn has_verbatim_unc_prefix(&self) -> bool {
126        matches!(self.prefix_kind(), Some(Utf8WindowsPrefix::VerbatimUNC(..)))
127    }
128
129    /// Returns true if represented path has a verbatim disk prefix (e.g. `\\?\C:`)
130    pub fn has_verbatim_disk_prefix(&self) -> bool {
131        matches!(self.prefix_kind(), Some(Utf8WindowsPrefix::VerbatimDisk(_)))
132    }
133
134    /// Returns true if represented path has a device NS prefix (e.g. `\\.\BrainInterface`)
135    pub fn has_device_ns_prefix(&self) -> bool {
136        matches!(self.prefix_kind(), Some(Utf8WindowsPrefix::DeviceNS(_)))
137    }
138
139    /// Returns true if represented path has a UNC prefix (e.g. `\\server\share`)
140    pub fn has_unc_prefix(&self) -> bool {
141        matches!(self.prefix_kind(), Some(Utf8WindowsPrefix::UNC(..)))
142    }
143
144    /// Returns true if represented path has a disk prefix (e.g. `C:`)
145    pub fn has_disk_prefix(&self) -> bool {
146        matches!(self.prefix_kind(), Some(Utf8WindowsPrefix::Disk(_)))
147    }
148
149    /// Returns true if there is a separator immediately after the prefix, or separator
150    /// starts the components if there is no prefix
151    ///
152    /// e.g. `C:\` and `\path` would return true whereas `\\?\path` would return false
153    pub fn has_physical_root(&self) -> bool {
154        self.inner.has_physical_root()
155    }
156
157    /// Returns true if there is a root separator without a [`Utf8WindowsComponent::RootDir`]
158    /// needing to be present. This is tied to prefixes like verbatim `\\?\` and UNC `\\`.
159    ///
160    /// Really, it's everything but a disk prefix of `C:` that provide an implicit root
161    pub fn has_implicit_root(&self) -> bool {
162        match self.prefix().map(|p| p.kind()) {
163            Some(Utf8WindowsPrefix::Disk(_)) | None => false,
164            Some(_) => true,
165        }
166    }
167}
168
169impl AsRef<[u8]> for Utf8WindowsComponents<'_> {
170    #[inline]
171    fn as_ref(&self) -> &[u8] {
172        self.as_str().as_bytes()
173    }
174}
175
176impl AsRef<str> for Utf8WindowsComponents<'_> {
177    #[inline]
178    fn as_ref(&self) -> &str {
179        self.as_str()
180    }
181}
182
183impl<T> AsRef<Utf8Path<T>> for Utf8WindowsComponents<'_>
184where
185    T: Utf8Encoding,
186{
187    #[inline]
188    fn as_ref(&self) -> &Utf8Path<T> {
189        Utf8Path::new(self.as_str())
190    }
191}
192
193impl fmt::Debug for Utf8WindowsComponents<'_> {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        struct DebugHelper<'a>(Utf8WindowsComponents<'a>);
196
197        impl fmt::Debug for DebugHelper<'_> {
198            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199                f.debug_list().entries(self.0.clone()).finish()
200            }
201        }
202
203        f.debug_tuple("Utf8WindowsComponents")
204            .field(&DebugHelper(self.clone()))
205            .finish()
206    }
207}
208
209impl<'a> Iterator for Utf8WindowsComponents<'a> {
210    type Item = <Self as Utf8Components<'a>>::Component;
211
212    fn next(&mut self) -> Option<Self::Item> {
213        self.inner
214            .next()
215            .map(|c| unsafe { Utf8WindowsComponent::from_utf8_unchecked(&c) })
216    }
217}
218
219impl DoubleEndedIterator for Utf8WindowsComponents<'_> {
220    fn next_back(&mut self) -> Option<Self::Item> {
221        self.inner
222            .next_back()
223            .map(|c| unsafe { Utf8WindowsComponent::from_utf8_unchecked(&c) })
224    }
225}
226
227impl iter::FusedIterator for Utf8WindowsComponents<'_> {}
228
229impl cmp::PartialEq for Utf8WindowsComponents<'_> {
230    #[inline]
231    fn eq(&self, other: &Self) -> bool {
232        PartialEq::eq(&self.inner, &other.inner)
233    }
234}
235
236impl cmp::Eq for Utf8WindowsComponents<'_> {}
237
238impl cmp::PartialOrd for Utf8WindowsComponents<'_> {
239    #[inline]
240    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
241        Some(self.cmp(other))
242    }
243}
244
245impl cmp::Ord for Utf8WindowsComponents<'_> {
246    #[inline]
247    fn cmp(&self, other: &Self) -> cmp::Ordering {
248        Ord::cmp(&self.inner, &other.inner)
249    }
250}