Skip to main content

normal_path/
public.rs

1use std::{
2    borrow::Cow,
3    collections::TryReserveError,
4    ffi::{OsStr, OsString},
5    path::{Components, Path, PathBuf, PrefixComponent},
6    str::FromStr,
7};
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12use super::{
13    imp,
14    trivial::{cast_box_unchecked, cast_ref_unchecked, ConvertError, Error, Normpath, NormpathBuf},
15};
16
17macro_rules! delegate {
18    ($platform:ident => $expr:expr) => {{
19        #[cfg($platform)]
20        {
21            Some($expr)
22        }
23        #[cfg(not($platform))]
24        {
25            None
26        }
27    }};
28}
29
30impl Normpath {
31    /// Wraps the root path `/` as a `Normpath` slice.
32    ///
33    /// This is a cost-free conversion.
34    #[cfg(any(unix, docsrs))]
35    #[cfg_attr(docsrs, doc(cfg(unix)))]
36    #[must_use]
37    #[inline]
38    pub fn unix_root() -> &'static Self {
39        // SAFETY: "/" is a normalized path
40        unsafe { cast_ref_unchecked(Path::new("/")) }
41    }
42
43    /// Wraps the root path as a `Normpath` slice, if there is one for the local
44    /// platform.
45    ///
46    /// This is equivalent to [`unix_root`] on Unix, while returning [`None`] on
47    /// other platforms.
48    ///
49    /// [`unix_root`]: Self::unix_root
50    #[must_use]
51    #[inline]
52    pub fn root() -> Option<&'static Self> {
53        delegate!(unix => Self::unix_root())
54    }
55
56    /// Validates that `path` is normalized to wrap it as a `Normpath` slice.
57    ///
58    /// Among all possible [`Error`] variants, this function always fails fast
59    /// with the first one encountered. See [`validate_canonical`] and
60    /// [`validate_parentless`] if certain variants are of particular interest.
61    ///
62    /// # Errors
63    ///
64    /// If `path` is not normalized, returns an [`Error`].
65    ///
66    /// # Examples
67    ///
68    /// ```rust,ignore-windows
69    /// use normal_path::{Normpath, Error};
70    ///
71    /// let norm = "/foo/bar";
72    /// let path1 = "foo/bar";
73    /// let path2 = "/foo/bar/";
74    /// let path3 = "/foo/../bar";
75    ///
76    /// assert!(Normpath::validate(norm).is_ok());
77    /// assert_eq!(Normpath::validate(path1), Err(Error::NotAbsolute));
78    /// assert_eq!(Normpath::validate(path2), Err(Error::NotCanonical));
79    /// assert_eq!(Normpath::validate(path3), Err(Error::ContainsParent));
80    /// ```
81    ///
82    /// [`validate_canonical`]: Self::validate_canonical
83    /// [`validate_parentless`]: Self::validate_parentless
84    #[inline]
85    pub fn validate<S: AsRef<OsStr> + ?Sized>(path: &S) -> Result<&Self, Error> {
86        imp::validate(Path::new(path))
87    }
88
89    /// Validates that `path` is normalized to wrap it as a `Normpath` slice,
90    /// with a focus on the canonicality of the path.
91    ///
92    /// As such, the function may search the entire path for non-canonical
93    /// patterns even with the presence of other errors. See [`validate`] if a
94    /// fast failure is preferred.
95    ///
96    /// # Errors
97    ///
98    /// If `path` is not normalized, returns an [`Error`] with a certain order
99    /// of precedence among all possible variants:
100    /// 1. [`Error::NotCanonical`]
101    /// 2. [`Error::ContainsParent`]
102    /// 3. [`Error::NotAbsolute`]
103    ///
104    /// # Notes on Windows
105    ///
106    /// On Windows, a parent directory component that can be normalized
107    /// lexically (e.g. `C:\foo\..`) is considered as [`Error::NotCanonical`]
108    /// instead of [`Error::ContainsParent`].
109    ///
110    /// See [crate documentation](crate) for more details about that.
111    ///
112    /// # Examples
113    ///
114    /// ```rust,ignore-windows
115    /// use normal_path::{Normpath, Error};
116    ///
117    /// let norm = "/foo/bar";
118    /// assert!(Normpath::validate_canonical(norm).is_ok());
119    ///
120    /// let path1 = "/foo/../bar/.";
121    /// let path2 = "/foo/../bar";
122    /// assert_eq!(Normpath::validate_canonical(path1), Err(Error::NotCanonical));
123    /// assert_eq!(Normpath::validate_canonical(path2), Err(Error::ContainsParent));
124    /// ```
125    ///
126    /// [`validate`]: Self::validate
127    #[inline]
128    pub fn validate_canonical<S: AsRef<OsStr> + ?Sized>(path: &S) -> Result<&Self, Error> {
129        imp::validate_canonical(Path::new(path))
130    }
131
132    /// Validates that `path` is normalized to wrap it as a `Normpath` slice,
133    /// with a focus on whether the path contains parent components that
134    /// cannot be normalized away.
135    ///
136    /// As such, the function may search the entire path for parent components
137    /// even with the presence of other errors. See [`validate`] if a fast
138    /// failure is preferred.
139    ///
140    /// # Errors
141    ///
142    /// If `path` is not normalized, returns an [`Error`] with a certain order
143    /// of precedence among all possible variants:
144    /// 1. [`Error::ContainsParent`]
145    /// 2. [`Error::NotCanonical`]
146    /// 3. [`Error::NotAbsolute`]
147    ///
148    /// # Notes on Windows
149    ///
150    /// On Windows, a parent directory component that can be normalized
151    /// lexically (e.g. `C:\foo\..`) is considered as [`Error::NotCanonical`]
152    /// instead of [`Error::ContainsParent`].
153    ///
154    /// See [crate documentation](crate) for more details about that.
155    ///
156    /// # Examples
157    ///
158    /// ```rust,ignore-windows
159    /// use normal_path::{Normpath, Error};
160    ///
161    /// let norm = "/foo/bar";
162    /// assert!(Normpath::validate_parentless(norm).is_ok());
163    ///
164    /// let path1 = "/foo/./bar/..";
165    /// let path2 = "/foo/./bar";
166    /// assert_eq!(Normpath::validate_parentless(path1), Err(Error::ContainsParent));
167    /// assert_eq!(Normpath::validate_parentless(path2), Err(Error::NotCanonical));
168    /// ```
169    ///
170    /// Windows deals with parent components differently:
171    ///
172    /// ```rust
173    /// # #[cfg(windows)] {
174    /// use normal_path::{Normpath, Error};
175    ///
176    /// let path1 = r"C:\foo\.\bar\..";
177    /// let path2 = r"C:\foo\.\bar";
178    /// assert_eq!(Normpath::validate_parentless(path1), Err(Error::NotCanonical));
179    /// assert_eq!(Normpath::validate_parentless(path2), Err(Error::NotCanonical));
180    ///
181    /// let path3 = r"C:\foo\.\bar\..\..\..";
182    /// assert_eq!(Normpath::validate_parentless(path3), Err(Error::ContainsParent));
183    /// # }
184    /// ```
185    ///
186    /// [`validate`]: Self::validate
187    #[inline]
188    pub fn validate_parentless<S: AsRef<OsStr> + ?Sized>(path: &S) -> Result<&Self, Error> {
189        imp::validate_parentless(Path::new(path))
190    }
191
192    /// Validates that `path` is normalized to wrap it as a `Normpath` slice
193    /// while trying to normalize non-canonical patterns.
194    ///
195    /// - If the path is already normalized, a borrowed slice is returned.
196    ///
197    /// - If the path only contains non-canonical patterns, an owned version is
198    ///   returned as the result of normalization.
199    ///
200    /// # Errors
201    ///
202    /// If `path` is not absolute or contains parent components that cannot be
203    /// normalized away, returns an [`Error`]. This implies [`NotCanonical`]
204    /// will never be returned.
205    ///
206    /// [`NotCanonical`]: Error::NotCanonical
207    ///
208    /// # Examples
209    ///
210    /// ```rust,ignore-windows
211    /// use std::borrow::Cow;
212    /// use normal_path::{Normpath, Error};
213    ///
214    /// let norm = Normpath::normalize("/foo/bar").unwrap();
215    /// let path1 = Normpath::normalize("foo/bar").unwrap_err();
216    /// let path2 = Normpath::normalize("/foo/./bar/").unwrap();
217    /// let path3 = Normpath::normalize("/foo/../bar").unwrap_err();
218    ///
219    /// assert!(matches!(norm, Cow::Borrowed(_)));
220    /// assert_eq!(&*norm, "/foo/bar");
221    ///
222    /// assert_eq!(path1, Error::NotAbsolute);
223    ///
224    /// assert!(matches!(path2, Cow::Owned(_)));
225    /// assert_eq!(&*path2, "/foo/bar");
226    ///
227    /// assert_eq!(path3, Error::ContainsParent);
228    /// ```
229    ///
230    /// Windows deals with parent components differently:
231    ///
232    /// ```rust
233    /// # #[cfg(windows)] {
234    /// use std::borrow::Cow;
235    /// use normal_path::{Normpath, Error};
236    ///
237    /// let path3 = Normpath::normalize(r"C:\foo\..\bar").unwrap();
238    /// let path4 = Normpath::normalize(r"C:\foo\..\bar\..\..").unwrap_err();
239    ///
240    /// assert!(matches!(path3, Cow::Owned(_)));
241    /// assert_eq!(&*path3, r"C:\bar");
242    ///
243    /// assert_eq!(path4, Error::ContainsParent);
244    /// # }
245    /// ```
246    #[inline]
247    pub fn normalize<S: AsRef<OsStr> + ?Sized>(path: &S) -> Result<Cow<'_, Self>, Error> {
248        imp::normalize_new_cow(Path::new(path))
249    }
250
251    /// Wraps `path` as a `Normpath` slice without any validation.
252    ///
253    /// # Safety
254    ///
255    /// `path` must be a normalized path, such that [`validate`] would succeed.
256    ///
257    /// # Examples
258    ///
259    /// ```rust,ignore-windows
260    /// use normal_path::Normpath;
261    ///
262    /// let path = "/foo/bar";
263    /// assert!(Normpath::validate(path).is_ok());
264    ///
265    /// // SAFETY: already validated
266    /// let norm = unsafe { Normpath::new_unchecked(path) };
267    /// ```
268    ///
269    /// [`validate`]: Self::validate
270    #[must_use]
271    #[inline]
272    pub unsafe fn new_unchecked<S: AsRef<OsStr> + ?Sized>(path: &S) -> &Self {
273        let path = Path::new(path.as_ref());
274        unsafe { cast_ref_unchecked(path) }
275    }
276
277    /// Returns the length of `self` in bytes.
278    #[must_use]
279    #[inline]
280    #[allow(clippy::len_without_is_empty)]
281    pub fn len(&self) -> usize {
282        self.0.as_os_str().len()
283    }
284
285    /// Yields the underlying [`Path`] slice.
286    #[must_use]
287    #[inline]
288    pub fn as_path(&self) -> &Path {
289        &self.0
290    }
291
292    /// Returns the `Normpath` without the final component, if the path doesn't
293    /// end with the root.
294    ///
295    /// This function effectively shadows [`Path::parent`] but is entirely
296    /// compatible in most cases. In case the original method is desired,
297    /// use `self.as_path().parent()` to invoke it.
298    #[must_use]
299    #[inline]
300    pub fn parent(&self) -> Option<&Self> {
301        let parent = self.0.parent()?;
302        // SAFETY: the parent of a normalized path is also normalized
303        Some(unsafe { cast_ref_unchecked(parent) })
304    }
305
306    /// Splits [`self.components`] into the prefix part and the rest.
307    ///
308    /// [`self.components`]: Path::components
309    ///
310    /// # Examples
311    ///
312    /// ```rust
313    /// # #[cfg(windows)] {
314    /// use std::path::{Component, Prefix};
315    /// use normal_path::Normpath;
316    ///
317    /// let path = Normpath::validate(r"C:\a").unwrap();
318    /// let (prefix, mut components) = path.windows_split_components();
319    ///
320    /// assert_eq!(prefix.kind(), Prefix::Disk(b'C'));
321    /// assert_eq!(components.next(), Some(Component::RootDir));
322    /// assert_eq!(components.next(), Some(Component::Normal("a".as_ref())));
323    /// assert_eq!(components.next(), None);
324    /// # }
325    /// ```
326    #[cfg(any(windows, docsrs))]
327    #[cfg_attr(docsrs, doc(cfg(windows)))]
328    pub fn windows_split_components(&self) -> (PrefixComponent<'_>, Components<'_>) {
329        use std::path::Component::Prefix;
330
331        let mut components = self.0.components();
332        let Some(Prefix(prefix)) = components.next() else {
333            unreachable!()
334        };
335
336        (prefix, components)
337    }
338
339    /// Returns the prefix component of `self`.
340    #[cfg(any(windows, docsrs))]
341    #[cfg_attr(docsrs, doc(cfg(windows)))]
342    #[must_use]
343    #[inline]
344    pub fn windows_prefix(&self) -> PrefixComponent<'_> {
345        self.windows_split_components().0
346    }
347
348    /// Splits [`self.components`] into the prefix part and the rest, if there
349    /// is a prefix.
350    ///
351    /// This is equivalent to [`windows_split_components`] on Windows, while
352    /// returning [`None`] on other platforms.
353    ///
354    /// [`self.components`]: Path::components
355    /// [`windows_split_components`]: Self::windows_split_components
356    ///
357    /// # Examples
358    ///
359    /// ```rust
360    /// use std::path::Component;
361    /// use normal_path::Normpath;
362    ///
363    /// let path = if cfg!(windows) { r"C:\a" } else { "/a" };
364    /// let norm = Normpath::validate(path).unwrap();
365    ///
366    /// let mut components = match norm.split_components() {
367    ///     Some((_, components)) => components,
368    ///     None => norm.components(),
369    /// };
370    ///
371    /// assert_eq!(components.next(), Some(Component::RootDir));
372    /// assert_eq!(components.next(), Some(Component::Normal("a".as_ref())));
373    /// assert_eq!(components.next(), None);
374    /// ```
375    #[must_use]
376    #[inline]
377    pub fn split_components(&self) -> Option<(PrefixComponent<'_>, Components<'_>)> {
378        delegate!(windows => self.windows_split_components())
379    }
380
381    /// Returns the prefix component of `self`, if there is one.
382    ///
383    /// This is equivalent to [`windows_prefix`] on Windows, while returning
384    /// [`None`] on other platforms.
385    ///
386    /// [`windows_prefix`]: Self::windows_prefix
387    #[must_use]
388    #[inline]
389    pub fn prefix(&self) -> Option<PrefixComponent<'_>> {
390        delegate!(windows => self.windows_prefix())
391    }
392
393    /// Creates an owned [`NormpathBuf`] with `path` adjoined to `self`, with
394    /// normalization.
395    ///
396    /// See [`PathBuf::push`] for more details on what it means to adjoin a
397    /// path.
398    ///
399    /// # Errors
400    ///
401    /// If the resulting path cannot be normalized, returns an [`Error`]. This
402    /// implies [`NotCanonical`] will never be returned.
403    ///
404    /// [`NotCanonical`]: Error::NotCanonical
405    #[inline]
406    pub fn checked_join<P: AsRef<Path>>(&self, path: P) -> Result<NormpathBuf, Error> {
407        let mut buf = self.0.to_path_buf();
408        imp::push(&mut buf, path.as_ref())?;
409
410        Ok(NormpathBuf(buf))
411    }
412
413    /// Returns a path that, when joined onto `base`, yields `self`.
414    ///
415    /// If `base` is not a prefix of `self`, returns [`None`].
416    ///
417    /// Compared to [`Path::strip_prefix`], this function only performs
418    /// *byte-level* comparison, skipping the parsing as an optimization.
419    ///
420    /// # Examples
421    ///
422    /// ```rust,ignore-windows
423    /// use std::path::Path;
424    /// use normal_path::Normpath;
425    ///
426    /// let norm = Normpath::validate("/foo/bar/baz").unwrap();
427    /// let base1 = Path::new("/foo/bar");
428    /// let base2 = Path::new("/foo/./bar");
429    /// let base3 = Path::new("/foo/ba");
430    ///
431    /// // strip_prefix parses paths and thus both are accepted
432    /// assert!(norm.strip_prefix(base1).is_ok());
433    /// assert!(norm.strip_prefix(base2).is_ok());
434    ///
435    /// // But quick_strip_prefix only compares bytes
436    /// assert!(norm.quick_strip_prefix(base1).is_some());
437    /// assert!(norm.quick_strip_prefix(base2).is_none());
438    ///
439    /// // Both functions are otherwise the same
440    /// assert!(norm.strip_prefix(base3).is_err());
441    /// assert!(norm.quick_strip_prefix(base3).is_none());
442    /// ```
443    #[must_use]
444    #[inline]
445    pub fn quick_strip_prefix<P: AsRef<Path>>(&self, base: P) -> Option<&Path> {
446        imp::strip(&self.0, base.as_ref())
447    }
448
449    /// Determines whether `base` is a prefix of `self`.
450    ///
451    /// Compared to [`Path::starts_with`], this function only performs
452    /// *byte-level* comparison, skipping the parsing as an optimization.
453    ///
454    /// # Examples
455    ///
456    /// ```rust,ignore-windows
457    /// use std::path::Path;
458    /// use normal_path::Normpath;
459    ///
460    /// let norm = Normpath::validate("/foo/bar/baz").unwrap();
461    /// let base1 = Path::new("/foo/bar");
462    /// let base2 = Path::new("/foo/./bar");
463    /// let base3 = Path::new("/foo/ba");
464    ///
465    /// // starts_with parses paths and thus both are accepted
466    /// assert!(norm.starts_with(base1));
467    /// assert!(norm.starts_with(base2));
468    ///
469    /// // But quick_starts_with only compares bytes
470    /// assert!(norm.quick_starts_with(base1));
471    /// assert!(!norm.quick_starts_with(base2));
472    ///
473    /// // Both functions are otherwise the same
474    /// assert!(!norm.starts_with(base3));
475    /// assert!(!norm.quick_starts_with(base3));
476    /// ```
477    #[must_use]
478    #[inline]
479    pub fn quick_starts_with<P: AsRef<Path>>(&self, base: P) -> bool {
480        imp::strip(&self.0, base.as_ref()).is_some()
481    }
482}
483
484impl NormpathBuf {
485    /// Creates a new `NormpathBuf` from the root path `/`.
486    #[cfg(unix)]
487    #[cfg_attr(docsrs, doc(cfg(unix)))]
488    #[must_use]
489    #[inline]
490    pub fn root() -> Self {
491        Self(PathBuf::from("/"))
492    }
493
494    /// Validates that `path` is normalized to create a `NormpathBuf` from it.
495    ///
496    /// This function is an owned version of [`Normpath::validate`]. Refer to it
497    /// for more details.
498    ///
499    /// # Errors
500    ///
501    /// If `path` is not normalized, returns an [`ConvertError`] containing the
502    /// original `path` instance unchanged.
503    ///
504    /// # Examples
505    ///
506    /// ```rust,ignore-windows
507    /// use normal_path::{NormpathBuf, Error};
508    ///
509    /// let norm = "/foo/bar";
510    /// let path1 = "foo/bar";
511    /// let path2 = "/foo/bar/";
512    /// let path3 = "/foo/../bar";
513    ///
514    /// assert!(NormpathBuf::validate(norm).is_ok());
515    ///
516    /// let e1 = NormpathBuf::validate(path1).unwrap_err();
517    /// assert_eq!((e1.error, e1.value), (Error::NotAbsolute, path1));
518    /// let e2 = NormpathBuf::validate(path2).unwrap_err();
519    /// assert_eq!((e2.error, e2.value), (Error::NotCanonical, path2));
520    /// let e3 = NormpathBuf::validate(path3).unwrap_err();
521    /// assert_eq!((e3.error, e3.value), (Error::ContainsParent, path3));
522    /// ```
523    #[inline]
524    pub fn validate<P>(path: P) -> Result<Self, ConvertError<P>>
525    where
526        P: AsRef<OsStr> + Into<OsString>,
527    {
528        match imp::validate(Path::new(&path)) {
529            Ok(_) => Ok(Self(PathBuf::from(path.into()))),
530            Err(e) => Err(ConvertError::new(e, path)),
531        }
532    }
533
534    /// Validates that `path` is normalized to create a `NormpathBuf` from it
535    /// while trying to normalize non-canonical patterns.
536    ///
537    /// This function will *mutate* the input `path` to normalize *every*
538    /// non-canonical pattern found, even with the presence of other errors.
539    ///
540    /// # Caveats
541    ///
542    /// This function mutates the input `path` *regardless* of the result. Use
543    /// [`Normpath::normalize`] or the corresponding [`TryFrom`] implementation
544    /// if mutation is not desired.
545    ///
546    /// # Errors
547    ///
548    /// If `path` is not absolute or contains parent components that cannot be
549    /// normalized away, returns an [`ConvertError`] containing the `path`,
550    /// with all non-canonicality removed. This implies [`NotCanonical`] will
551    /// never be returned.
552    ///
553    /// # Examples
554    ///
555    /// ```rust,ignore-windows
556    /// use std::path::PathBuf;
557    /// use normal_path::{Error, NormpathBuf};
558    ///
559    /// let path1 = PathBuf::from("//foo/./bar/");
560    /// let norm1 = NormpathBuf::normalize(path1).unwrap();
561    /// assert_eq!(&norm1, "/foo/bar");
562    ///
563    /// let path2 = PathBuf::from(".//foo/../bar/");
564    /// let err2 = NormpathBuf::normalize(path2).unwrap_err();
565    /// assert_eq!(&err2.value, "foo/../bar");
566    /// ```
567    ///
568    /// The results are different on Windows:
569    ///
570    /// ```rust
571    /// # #[cfg(windows)] {
572    /// use std::path::PathBuf;
573    /// use normal_path::{Error, NormpathBuf};
574    ///
575    /// let path1 = PathBuf::from(r"C:\foo/.\bar\");
576    /// let norm1 = NormpathBuf::normalize(path1).unwrap();
577    /// assert_eq!(&norm1, r"C:\foo\bar");
578    ///
579    /// let path2 = PathBuf::from(r"./\foo\../bar\..");
580    /// let err2 = NormpathBuf::normalize(path2).unwrap_err();
581    /// assert_eq!(&err2.value, "");
582    /// # }
583    /// ```
584    ///
585    /// [`NotCanonical`]: Error::NotCanonical
586    #[inline]
587    pub fn normalize(mut path: PathBuf) -> Result<Self, ConvertError<PathBuf>> {
588        match imp::normalize(&mut path) {
589            Ok(_) => Ok(Self(path)),
590            Err(e) => Err(ConvertError::new(e, path)),
591        }
592    }
593
594    /// Creates a `NormpathBuf` from `path` without any validation.
595    ///
596    /// # Safety
597    ///
598    /// `path` must be a normalized path, such that [`validate`] would succeed.
599    ///
600    /// # Examples
601    ///
602    /// ```rust,ignore-windows
603    /// use normal_path::NormpathBuf;
604    ///
605    /// let path = "/foo/bar";
606    /// assert!(NormpathBuf::validate(path).is_ok());
607    ///
608    /// // SAFETY: already validated
609    /// let norm = unsafe { NormpathBuf::new_unchecked(path) };
610    /// ```
611    ///
612    /// [`validate`]: Self::validate
613    #[must_use]
614    #[inline]
615    pub unsafe fn new_unchecked<S: Into<OsString>>(path: S) -> Self {
616        Self(PathBuf::from(path.into()))
617    }
618
619    /// Extends `self` with `path`, with normalization.
620    ///
621    /// See [`PathBuf::push`] for more details on how `self` is extended with
622    /// `path`.
623    ///
624    /// Consider using [`Normpath::checked_join`] if you need a new
625    /// `NormpathBuf` instead of using this function on a cloned `NormpathBuf`.
626    ///
627    /// # Errors
628    ///
629    /// If the resulting path cannot be normalized, returns an [`Error`]. This
630    /// implies [`NotCanonical`] will never be returned.
631    ///
632    /// [`NotCanonical`]: Error::NotCanonical
633    #[inline]
634    pub fn push<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
635        imp::push(&mut self.0, path.as_ref())
636    }
637
638    /// Truncates `self` to [`self.parent`].
639    ///
640    /// Returns `false` and does nothing if [`self.parent`] is [`None`].
641    ///
642    /// [`self.parent`]: Normpath::parent
643    #[inline]
644    pub fn pop(&mut self) -> bool {
645        self.0.pop()
646    }
647
648    /// Coerces to a [`Normpath`] slice.
649    #[must_use]
650    #[inline]
651    pub fn as_normpath(&self) -> &Normpath {
652        self.as_ref()
653    }
654
655    /// Converts the `NormpathBuf` into a [boxed](Box) [`Normpath`].
656    #[must_use]
657    #[inline]
658    pub fn into_boxed_path(self) -> Box<Normpath> {
659        let value = self.0.into_boxed_path();
660        // SAFETY: the path is a Normpath per se
661        unsafe { cast_box_unchecked(value) }
662    }
663
664    /// Consumes the `NormpathBuf`, yielding the underlying [`PathBuf`]
665    /// instance.
666    #[must_use]
667    #[inline]
668    pub fn into_path_buf(self) -> PathBuf {
669        self.0
670    }
671
672    /// Consumes the `NormpathBuf`, yielding its internal [`OsString`] storage.
673    #[must_use]
674    #[inline]
675    pub fn into_os_string(self) -> OsString {
676        self.0.into_os_string()
677    }
678
679    /// Invokes [`capacity`](OsString::capacity) on the internal [`OsString`]
680    /// storage.
681    #[inline]
682    pub fn capacity(&self) -> usize {
683        self.0.capacity()
684    }
685
686    /// Invokes [`reserve`](OsString::reserve) on the internal [`OsString`]
687    /// storage.
688    #[inline]
689    pub fn reserve(&mut self, additional: usize) {
690        self.0.reserve(additional)
691    }
692
693    /// Invokes [`reserve_exact`](OsString::reserve_exact) on the internal
694    /// [`OsString`] storage.
695    #[inline]
696    pub fn reserve_exact(&mut self, additional: usize) {
697        self.0.reserve_exact(additional)
698    }
699
700    /// Invokes [`try_reserve`](OsString::try_reserve) on the internal
701    /// [`OsString`] storage.
702    #[inline]
703    pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
704        self.0.try_reserve(additional)
705    }
706
707    /// Invokes [`try_reserve_exact`](OsString::try_reserve_exact) on the
708    /// internal [`OsString`] storage.
709    #[inline]
710    pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
711        self.0.try_reserve_exact(additional)
712    }
713
714    /// Invokes [`shrink_to_fit`](OsString::shrink_to_fit) on the internal
715    /// [`OsString`] storage.
716    #[inline]
717    pub fn shrink_to_fit(&mut self) {
718        self.0.shrink_to_fit()
719    }
720
721    /// Invokes [`shrink_to`](OsString::shrink_to) on the internal [`OsString`]
722    /// storage.
723    #[inline]
724    pub fn shrink_to(&mut self, min_capacity: usize) {
725        self.0.shrink_to(min_capacity)
726    }
727}
728
729macro_rules! impl_try_from {
730    (owned over <$($life:lifetime)?> $t:ty) => {
731        impl<$($life)?> TryFrom<$t> for NormpathBuf {
732            type Error = ConvertError<$t>;
733
734            /// Validates that `value` is normalized to create a [`NormpathBuf`]
735            /// from it.
736            ///
737            /// Unlike [`NormpathBuf::normalize`], this function never mutates
738            /// the input `value`.
739            fn try_from(value: $t) -> Result<Self, Self::Error> {
740                imp::normalize_new_buf(value)
741            }
742        }
743    };
744    (&$life:lifetime $t:ty) => {
745        impl<$life> TryFrom<&$life $t> for NormpathBuf {
746            type Error = ConvertError<&$life $t>;
747
748            /// Validates that `value` is normalized to create a [`NormpathBuf`]
749            /// from it.
750            fn try_from(value: &$life $t) -> Result<Self, Self::Error> {
751                imp::normalize_new_buf(PathBuf::from(value))
752                    .map_err(|e| ConvertError::new(e.error, value))
753            }
754        }
755    };
756}
757
758impl_try_from!(owned over <> String);
759impl_try_from!(owned over <> OsString);
760impl_try_from!(owned over <> PathBuf);
761impl_try_from!(owned over <> Box<Path>);
762impl_try_from!(owned over <'a> Cow<'a, Path>);
763
764impl_try_from!(&'a str);
765impl_try_from!(&'a String);
766impl_try_from!(&'a OsStr);
767impl_try_from!(&'a OsString);
768impl_try_from!(&'a Path);
769impl_try_from!(&'a PathBuf);
770
771#[cfg(feature = "serde")]
772#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
773impl Serialize for Normpath {
774    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
775        self.as_path().serialize(serializer)
776    }
777}
778
779#[cfg(feature = "serde")]
780#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
781impl Serialize for NormpathBuf {
782    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
783        self.as_path().serialize(serializer)
784    }
785}
786
787impl FromStr for NormpathBuf {
788    type Err = ConvertError<String>;
789
790    fn from_str(s: &str) -> Result<Self, Self::Err> {
791        imp::normalize_new_buf(s.to_string())
792    }
793}
794
795impl FromStr for Box<Normpath> {
796    type Err = ConvertError<String>;
797
798    fn from_str(s: &str) -> Result<Self, Self::Err> {
799        imp::normalize_new_buf(s.to_string()).map(|p| p.into_boxed_path())
800    }
801}
802
803#[cfg(feature = "serde")]
804#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
805impl<'a, 'de: 'a> Deserialize<'de> for &'a Normpath {
806    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
807        let path = <&Path>::deserialize(deserializer)?;
808        imp::validate(path).map_err(serde::de::Error::custom)
809    }
810}
811
812#[cfg(feature = "serde")]
813#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
814impl<'de> Deserialize<'de> for NormpathBuf {
815    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
816        let path = PathBuf::deserialize(deserializer)?;
817        imp::normalize_new_buf(path).map_err(serde::de::Error::custom)
818    }
819}
820
821#[cfg(feature = "serde")]
822#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
823impl<'de> Deserialize<'de> for Box<Normpath> {
824    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
825        let path = PathBuf::deserialize(deserializer)?;
826        imp::normalize_new_buf(path)
827            .map(|p| p.into_boxed_path())
828            .map_err(serde::de::Error::custom)
829    }
830}
831
832/// Normalizes all non-canonical patterns of `path`.
833///
834/// This function does not try to remove parent components on Unix, but does so
835/// on Windows. See [crate documentation](crate) for more details about that.
836///
837/// Consider using [`NormpathBuf`] (or [`Normpath`]) if validation should be
838/// type-checked and thus enforced by the compiler.
839///
840/// # Examples
841///
842/// ```rust,ignore-windows
843/// # use normal_path::canonicalize_lexically;
844/// use std::path::PathBuf;
845///
846/// let path1 = PathBuf::from("//foo/./bar/");
847/// let norm1 = canonicalize_lexically(path1);
848/// assert_eq!(&norm1, "/foo/bar");
849///
850/// let path2 = PathBuf::from(".//foo/../bar/");
851/// let norm2 = canonicalize_lexically(path2);
852/// assert_eq!(&norm2, "foo/../bar");
853/// ```
854///
855/// Windows deals with parent components differently:
856///
857/// ```rust
858/// # #[cfg(windows)] {
859/// # use normal_path::canonicalize_lexically;
860/// use std::path::PathBuf;
861///
862/// let path1 = PathBuf::from(r"C:\foo/.\bar\");
863/// let norm1 = canonicalize_lexically(path1);
864/// assert_eq!(&norm1, r"C:\foo\bar");
865///
866/// let path2 = PathBuf::from(r"./\foo\../bar\..");
867/// let norm2 = canonicalize_lexically(path2);
868/// assert_eq!(&norm2, "");
869/// # }
870/// ```
871#[must_use]
872#[inline]
873pub fn canonicalize_lexically(path: PathBuf) -> PathBuf {
874    NormpathBuf::normalize(path)
875        .map(|it| it.into_path_buf())
876        .unwrap_or_else(|e| e.value)
877}
878
879#[cfg(test)]
880mod tests {
881    use std::{iter, ops::RangeBounds, path::Component};
882
883    use fastrand::Rng;
884
885    use crate::draw;
886
887    use super::{ConvertError as Ec, Error as E, *};
888
889    const MIN_ABS: usize = if cfg!(unix) { 1 } else { 3 };
890
891    #[cfg(unix)]
892    fn make_root() -> NormpathBuf {
893        NormpathBuf::root()
894    }
895
896    #[cfg(windows)]
897    fn make_root() -> NormpathBuf {
898        let letter = char::from(fastrand::u8(b'A'..=b'Z'));
899        NormpathBuf::try_from(format!(r"{letter}:\")).unwrap()
900    }
901
902    fn make_paths(
903        bound: impl RangeBounds<usize> + Clone,
904        mut draw: impl FnMut(usize) -> String,
905    ) -> impl Iterator<Item = String> {
906        let mut rng = Rng::new();
907        iter::from_fn(move || Some(draw(rng.usize(bound.clone()))))
908    }
909
910    #[cfg(unix)]
911    fn to_canonical(path: &Path) -> PathBuf {
912        path.components()
913            .filter(|c| !matches!(c, Component::CurDir))
914            .collect()
915    }
916
917    #[cfg(windows)]
918    fn to_canonical(path: &Path) -> PathBuf {
919        use std::path::Prefix;
920
921        let mut out = PathBuf::with_capacity(path.as_os_str().len());
922        for component in path.components() {
923            use {std::path::Prefix::*, Component::*};
924            match component {
925                Prefix(component) => match component.kind() {
926                    Disk(c) => {
927                        let letter = char::from(c.to_ascii_uppercase());
928                        out.push(format!("{letter}:"));
929                    }
930                    DeviceNS(name) => {
931                        let name = name.to_str().unwrap();
932                        out.push(format!(r"\\.\{name}"));
933                    }
934                    UNC(server, share) => {
935                        let server = server.to_str().unwrap();
936                        let share = share.to_str().unwrap();
937                        out.push(format!(r"\\{server}\{share}"));
938                    }
939                    _ => return path.into(),
940                },
941                RootDir => out.push("\\"),
942                CurDir => continue,
943                ParentDir if out.file_name().is_some() => assert!(out.pop()),
944                ParentDir => out.push(".."),
945                Normal(name) => out.push(name),
946            }
947        }
948
949        let mut components = out.components();
950        let first = components.next();
951        let tail = components.as_path();
952
953        let is_phony = match first {
954            Some(Component::Prefix(prefix)) => {
955                matches!(prefix.kind(), Prefix::DeviceNS(_) | Prefix::UNC(_, _))
956                    && tail.as_os_str() == "\\"
957            }
958            _ => false,
959        };
960
961        if is_phony {
962            let mut bytes = std::mem::take(&mut out)
963                .into_os_string()
964                .into_encoded_bytes();
965
966            bytes.pop();
967            out = unsafe { OsString::from_encoded_bytes_unchecked(bytes) }.into();
968        }
969
970        out
971    }
972
973    fn is_canonical(path: &Path) -> bool {
974        path.as_os_str() == to_canonical(path).as_os_str()
975    }
976
977    #[cfg(unix)]
978    fn is_parentless(path: &Path) -> bool {
979        path.components()
980            .all(|c| !matches!(c, Component::ParentDir))
981    }
982
983    #[cfg(windows)]
984    fn is_parentless(path: &Path) -> bool {
985        use Component::*;
986
987        let mut components = path.components();
988        let start = match components.next() {
989            Some(Prefix(it)) if it.kind().is_verbatim() => return true,
990            Some(ParentDir) => return false,
991            Some(Normal(_)) => 1u32,
992            Some(_) => 0u32,
993            _ => return true,
994        };
995
996        path.components()
997            .map(|c| match c {
998                ParentDir => -1,
999                Normal(_) => 1,
1000                _ => 0,
1001            })
1002            .try_fold(start, |acc, step| acc.checked_add_signed(step))
1003            .is_some()
1004    }
1005
1006    fn is_normalized(path: &Path) -> bool {
1007        path.is_absolute() && is_canonical(path) && is_parentless(path)
1008    }
1009
1010    fn into_source<T>(error: Ec<T>) -> E {
1011        error.error
1012    }
1013
1014    #[cfg(unix)]
1015    #[test]
1016    pub fn root() {
1017        Normpath::validate("/").unwrap();
1018        NormpathBuf::try_from("/").unwrap();
1019    }
1020
1021    #[cfg(windows)]
1022    #[test]
1023    pub fn root() {
1024        let paths = ('A'..='Z').map(|c| format!(r"{c}:\"));
1025        for path in paths {
1026            Normpath::validate(&path).unwrap();
1027            NormpathBuf::try_from(path).unwrap();
1028        }
1029    }
1030
1031    #[test]
1032    pub fn empty() {
1033        assert_eq!(Normpath::validate(""), Err(E::NotAbsolute));
1034        assert_eq!(
1035            NormpathBuf::try_from("").map_err(into_source),
1036            Err(E::NotAbsolute),
1037        );
1038    }
1039
1040    fn test_normalize(path: &str) -> OsString {
1041        let in_place = NormpathBuf::normalize(path.into()).map(|p| p.into_os_string());
1042        let new = Normpath::normalize(path).map(|p| p.into_owned().into_os_string());
1043        match (in_place, new) {
1044            (Ok(in_place), Ok(new)) => {
1045                assert_eq!(in_place, new, "inconsistent on {:?}", path);
1046                in_place
1047            }
1048            (Err(ec), Err(_)) => ec.value.into(),
1049            (a, b) => panic!(
1050                "inconsistent on {:?}: in-place = {:?}, new = {:?}",
1051                path, a, b
1052            ),
1053        }
1054    }
1055
1056    #[cfg(unix)]
1057    #[test]
1058    pub fn example_normalize() {
1059        assert_eq!(test_normalize("/foo/bar"), "/foo/bar");
1060        assert_eq!(test_normalize("//foo/./..//bar//"), "/foo/../bar");
1061
1062        assert_eq!(test_normalize("foo/bar"), "foo/bar");
1063        assert_eq!(test_normalize(".//foo/.//bar/..//"), "foo/bar/..");
1064    }
1065
1066    #[cfg(windows)]
1067    #[test]
1068    pub fn example_normalize() {
1069        assert_eq!(test_normalize(r"C:\foo\bar"), r"C:\foo\bar");
1070        assert_eq!(test_normalize(r"c:/foo/.."), r"C:\");
1071        assert_eq!(test_normalize(r"c:\/foo\..\./../bar/.."), r"C:\..");
1072
1073        assert_eq!(test_normalize(r"foo\bar"), r"foo\bar");
1074        assert_eq!(test_normalize(r".\/foo\./\../"), r"");
1075        assert_eq!(test_normalize(r".\/foo/..\bar/../..//"), r"..");
1076
1077        assert_eq!(test_normalize(r"\/.\dev\foo"), r"\\.\dev\foo");
1078        assert_eq!(test_normalize(r"\\./dev"), r"\\.\dev");
1079        assert_eq!(test_normalize(r"//./dev/foo\/bar\../"), r"\\.\dev\foo");
1080
1081        assert_eq!(test_normalize(r"\/s\s\foo\bar"), r"\\s\s\foo\bar");
1082        assert_eq!(test_normalize(r"\\s/s\foo/\.\..\bar\/"), r"\\s\s\bar");
1083        assert_eq!(test_normalize(r"//s\s/foo\../\./../\bar/.."), r"\\s\s\..");
1084
1085        assert_eq!(test_normalize(r"C:foo\bar"), r"C:foo\bar");
1086        assert_eq!(test_normalize(r"c:.\\foo\../\bar//"), r"C:bar");
1087        assert_eq!(test_normalize(r"c:.\foo\../bar/.."), r"C:");
1088
1089        // No normalization will ever be applied to verbatim paths.
1090        assert_eq!(
1091            test_normalize(r"\\?\C:\foo/..\/bar/."),
1092            r"\\?\C:\foo/..\/bar/."
1093        );
1094        assert_eq!(
1095            test_normalize(r"\\?\UNC\s\s\foo\./..\bar/"),
1096            r"\\?\UNC\s\s\foo\./..\bar/"
1097        );
1098    }
1099
1100    #[test]
1101    pub fn normalize() {
1102        let paths = make_paths(1..64, draw::common);
1103        for path in paths.take(1024) {
1104            let reference = to_canonical(Path::new(&path));
1105            let ours = test_normalize(&path);
1106
1107            assert_eq!(reference.as_os_str(), ours, "original: {path:?}");
1108        }
1109    }
1110
1111    #[cfg(unix)]
1112    fn test_push(subject: &mut NormpathBuf, reference: &mut PathBuf, component: &str) {
1113        if is_parentless(component.as_ref()) {
1114            reference.push(component);
1115            subject.push(component).unwrap();
1116
1117            assert_eq!(subject.as_path(), reference.as_path());
1118        } else {
1119            let copy = subject.clone();
1120            let error = subject.push(component).unwrap_err();
1121            assert_eq!(error, E::ContainsParent);
1122            assert_eq!(*subject, copy);
1123        }
1124    }
1125
1126    #[cfg(windows)]
1127    fn test_push(subject: &mut NormpathBuf, reference: &mut PathBuf, component: &str) {
1128        let peek = reference.join(component);
1129        if peek.is_absolute() && is_parentless(&peek) {
1130            *reference = to_canonical(&peek);
1131            subject.push(component).unwrap();
1132
1133            assert_eq!(subject.as_path(), reference.as_path());
1134        } else {
1135            let copy = subject.clone();
1136            let error = subject.push(component).unwrap_err();
1137            if !peek.is_absolute() {
1138                assert_eq!(error, E::NotAbsolute);
1139            } else {
1140                assert_eq!(error, E::ContainsParent);
1141            }
1142            assert_eq!(*subject, copy);
1143        }
1144    }
1145
1146    #[test]
1147    pub fn push_relative() {
1148        for _ in 0..128 {
1149            let components = make_paths(1..16, draw::relative);
1150
1151            let mut path = make_root();
1152            let mut twin = path.clone().into_path_buf();
1153            for component in components.take(64) {
1154                test_push(&mut path, &mut twin, &component);
1155            }
1156        }
1157    }
1158
1159    #[test]
1160    pub fn push_absolute() {
1161        for _ in 0..128 {
1162            let components = make_paths(MIN_ABS..32, draw::absolute);
1163
1164            let mut path = make_root();
1165            let mut twin = path.clone().into_path_buf();
1166            for component in components.take(32) {
1167                test_push(&mut path, &mut twin, &component);
1168            }
1169        }
1170    }
1171
1172    #[cfg(windows)]
1173    #[test]
1174    pub fn push_partial() {
1175        for path in make_paths(MIN_ABS..64, draw::normal).take(128) {
1176            let roots = make_paths(1..32, draw::root_only);
1177
1178            let mut path = NormpathBuf::try_from(path).unwrap();
1179            let mut twin = path.clone().into_path_buf();
1180            for root in roots.take(32) {
1181                test_push(&mut path, &mut twin, &root);
1182            }
1183        }
1184
1185        for path in make_paths(MIN_ABS..64, draw::normal).take(64) {
1186            let prefixes = iter::from_fn(|| Some(draw::disk_only()));
1187
1188            let mut path = NormpathBuf::try_from(path).unwrap();
1189            let mut twin = path.clone().into_path_buf();
1190            for prefix in prefixes.take(16) {
1191                test_push(&mut path, &mut twin, &prefix);
1192            }
1193        }
1194    }
1195
1196    #[cfg(unix)]
1197    fn make_normal_paths() -> impl Iterator<Item = String> {
1198        make_paths(MIN_ABS..64, draw::normal).take(256)
1199    }
1200
1201    #[cfg(windows)]
1202    fn make_normal_paths() -> impl Iterator<Item = String> {
1203        let genuine = make_paths(MIN_ABS..64, draw::normal);
1204        let verbatim = make_paths(7..64, draw::verbatim);
1205        genuine.take(224).chain(verbatim.take(32))
1206    }
1207
1208    fn test_normal(path: &str) {
1209        let ref_validated = Normpath::validate(path).unwrap();
1210        assert!(is_normalized(ref_validated.as_path()));
1211        assert_eq!(ref_validated, Path::new(path));
1212
1213        let ref_validated_c = Normpath::validate_canonical(path).unwrap();
1214        assert_eq!(ref_validated_c, ref_validated);
1215
1216        let ref_validated_p = Normpath::validate_parentless(path).unwrap();
1217        assert_eq!(ref_validated_p, ref_validated);
1218
1219        let ref_normalized = Normpath::normalize(path).unwrap();
1220        assert_eq!(&*ref_normalized, ref_validated);
1221
1222        let buf_validated = NormpathBuf::validate(path).unwrap();
1223        assert_eq!(&buf_validated, ref_validated);
1224
1225        let buf_normalized = NormpathBuf::normalize(PathBuf::from(path)).unwrap();
1226        assert_eq!(&buf_normalized, ref_validated);
1227
1228        let buf_converted = NormpathBuf::try_from(path).unwrap();
1229        assert_eq!(&buf_converted, ref_validated);
1230    }
1231
1232    #[cfg(unix)]
1233    #[test]
1234    pub fn examples_normal() {
1235        test_normal("/");
1236        test_normal("/foo/bar");
1237    }
1238
1239    #[cfg(windows)]
1240    #[test]
1241    pub fn examples_normal() {
1242        test_normal(r"C:\");
1243        test_normal(r"C:\foo\bar");
1244
1245        test_normal(r"\\.\dev");
1246        test_normal(r"\\.\dev\foo\bar");
1247        test_normal(r"\\s\s");
1248        test_normal(r"\\s\s\foo\bar");
1249
1250        test_normal(r"\\?\C:.\../foo\bar//");
1251        test_normal(r"\\?\UNC\s\s/foo\/.\..\bar\/");
1252    }
1253
1254    #[test]
1255    pub fn normal() {
1256        for path in make_normal_paths() {
1257            test_normal(&path);
1258        }
1259    }
1260
1261    fn test_err_single(path: &str, error: E) {
1262        assert_eq!(Normpath::validate(&path), Err(error));
1263        assert_eq!(Normpath::validate_canonical(&path), Err(error));
1264        assert_eq!(Normpath::validate_parentless(&path), Err(error));
1265
1266        assert_eq!(
1267            NormpathBuf::validate(&path).map_err(into_source),
1268            Err(error),
1269        );
1270        assert_eq!(
1271            NormpathBuf::normalize(PathBuf::from(&path)).map_err(into_source),
1272            Err(error),
1273        );
1274
1275        let conv_err = NormpathBuf::try_from(path).unwrap_err();
1276        assert_eq!(conv_err.error, error);
1277        assert_eq!(conv_err.value, path);
1278    }
1279
1280    fn test_err_noncanonical(path: &str, canonical: impl AsRef<Path>) {
1281        assert_eq!(Normpath::validate(&path), Err(E::NotCanonical));
1282        assert_eq!(Normpath::validate_canonical(&path), Err(E::NotCanonical));
1283        assert_eq!(Normpath::validate_parentless(&path), Err(E::NotCanonical));
1284
1285        assert_eq!(
1286            NormpathBuf::validate(&path).map_err(into_source),
1287            Err(E::NotCanonical),
1288        );
1289
1290        let ref_normalized = Normpath::normalize(&path).unwrap();
1291        assert_eq!(&*ref_normalized, canonical.as_ref());
1292
1293        let buf_normalized = NormpathBuf::normalize(PathBuf::from(&path)).unwrap();
1294        assert_eq!(&buf_normalized, canonical.as_ref());
1295
1296        let buf_converted = NormpathBuf::try_from(path.to_string()).unwrap();
1297        assert_eq!(&buf_converted, canonical.as_ref());
1298    }
1299
1300    #[cfg(unix)]
1301    #[test]
1302    pub fn examples_err_single_relative() {
1303        test_err_single("", E::NotAbsolute);
1304        test_err_single("foo/bar", E::NotAbsolute);
1305    }
1306
1307    #[cfg(windows)]
1308    #[test]
1309    pub fn examples_err_single_relative() {
1310        test_err_single("", E::NotAbsolute);
1311        test_err_single(r"foo\bar", E::NotAbsolute);
1312
1313        test_err_single(r"\", E::NotAbsolute);
1314        test_err_single(r"\foo\bar", E::NotAbsolute);
1315
1316        test_err_single(r"C:", E::NotAbsolute);
1317        test_err_single(r"C:foo\bar", E::NotAbsolute);
1318    }
1319
1320    #[test]
1321    pub fn err_single_relative() {
1322        let paths = make_paths(1..64, draw::relative)
1323            .filter(|p| is_parentless(p.as_ref()) && is_canonical(p.as_ref()));
1324
1325        for path in paths.take(256) {
1326            test_err_single(&path, E::NotAbsolute);
1327        }
1328    }
1329
1330    #[cfg(unix)]
1331    #[test]
1332    pub fn examples_err_single_parent() {
1333        test_err_single("/..", E::ContainsParent);
1334        test_err_single("/foo/../bar", E::ContainsParent);
1335    }
1336
1337    #[cfg(windows)]
1338    #[test]
1339    pub fn examples_err_single_parent() {
1340        test_err_single(r"C:\..", E::ContainsParent);
1341
1342        test_err_single(r"\\.\dev\..", E::ContainsParent);
1343        test_err_single(r"\\s\s\..", E::ContainsParent);
1344    }
1345
1346    #[test]
1347    pub fn err_single_parent() {
1348        let paths = make_paths(MIN_ABS..64, draw::absolute)
1349            .filter(|p| !is_parentless(Path::new(p)) && is_canonical(Path::new(p)));
1350
1351        for path in paths.take(256) {
1352            test_err_single(&path, E::ContainsParent);
1353        }
1354    }
1355
1356    #[cfg(unix)]
1357    #[test]
1358    pub fn examples_err_single_noncanonical() {
1359        test_err_noncanonical("//", "/");
1360        test_err_noncanonical("/.", "/");
1361        test_err_noncanonical("/foo//bar", "/foo/bar");
1362        test_err_noncanonical("/foo/./bar", "/foo/bar");
1363        test_err_noncanonical("/foo/bar/", "/foo/bar");
1364        test_err_noncanonical("/.../..../", "/.../....");
1365    }
1366
1367    #[cfg(windows)]
1368    #[test]
1369    pub fn examples_err_single_noncanonical() {
1370        test_err_noncanonical(r"c:\", r"C:\");
1371        test_err_noncanonical(r"C:\\", r"C:\");
1372        test_err_noncanonical(r"C:\.", r"C:\");
1373        test_err_noncanonical(r"C:\foo\..", r"C:\");
1374        test_err_noncanonical(r"C:/foo/bar", r"C:\foo\bar");
1375        test_err_noncanonical(r"C:\foo\\bar", r"C:\foo\bar");
1376        test_err_noncanonical(r"C:\foo\.\bar", r"C:\foo\bar");
1377        test_err_noncanonical(r"C:\foo\bar\", r"C:\foo\bar");
1378        test_err_noncanonical(r"C:/.../....", r"C:\...\....");
1379
1380        test_err_noncanonical(r"\\.\dev\\", r"\\.\dev\");
1381        test_err_noncanonical(r"\\.\dev\.", r"\\.\dev\");
1382        test_err_noncanonical(r"\\s\s\\", r"\\s\s\");
1383        test_err_noncanonical(r"\\s\s\.", r"\\s\s\");
1384    }
1385
1386    #[test]
1387    pub fn err_single_noncanonical() {
1388        let paths = make_paths(MIN_ABS..64, draw::absolute)
1389            .filter(|p| !is_canonical(Path::new(p)) && is_parentless(Path::new(p)));
1390
1391        for path in paths.take(256) {
1392            test_err_noncanonical(&path, to_canonical(path.as_ref()));
1393        }
1394    }
1395
1396    fn test_err_preference(path: &str) {
1397        assert_eq!(Normpath::validate_canonical(path), Err(E::NotCanonical));
1398        assert_eq!(Normpath::validate_parentless(path), Err(E::ContainsParent));
1399    }
1400
1401    #[cfg(unix)]
1402    #[test]
1403    pub fn examples_err_preference() {
1404        test_err_preference("/foo/../bar/");
1405        test_err_preference("//foo/../bar");
1406    }
1407
1408    #[cfg(windows)]
1409    #[test]
1410    pub fn examples_err_preference() {
1411        test_err_preference(r"C:\..\foo\bar\");
1412        test_err_preference(r"C:\\foo\..\..\bar");
1413        test_err_preference(r"C:\foo\..\..");
1414
1415        test_err_preference(r"\\.\dev\..\foo\bar\");
1416        test_err_preference(r"\\.\dev\\foo\..\..\bar");
1417        test_err_preference(r"\\s\s\..\foo\bar\");
1418        test_err_preference(r"\\s\s\\foo\..\..\bar");
1419    }
1420
1421    #[test]
1422    pub fn err_preference() {
1423        let paths = make_paths(MIN_ABS..64, draw::absolute)
1424            .filter(|p| !is_canonical(Path::new(p)) && !is_parentless(Path::new(p)));
1425
1426        for path in paths.take(256) {
1427            test_err_preference(&path);
1428        }
1429    }
1430}