Skip to main content

normal_path/
trivial.rs

1use std::{
2    borrow::{Borrow, Cow},
3    cmp,
4    convert::Infallible,
5    ffi::{OsStr, OsString},
6    fmt::{self, Debug, Display},
7    hash::{Hash, Hasher},
8    ops::Deref,
9    path::{Path, PathBuf},
10    ptr,
11    rc::Rc,
12    sync::Arc,
13};
14
15/// The error type indicating why a path cannot be a valid [`Normpath`].
16///
17/// # Notes on Windows
18///
19/// On Windows, a parent directory component that can be normalized away (e.g.
20/// `C:\foo\..`) is not considered as [`ContainsParent`], but instead as
21/// [`NotCanonical`].
22///
23/// See [crate documentation](crate) for more details about that.
24///
25/// [`ContainsParent`]: Self::ContainsParent
26/// [`NotCanonical`]: Self::NotCanonical
27#[derive(Debug, Copy, Clone, PartialEq, Eq)]
28pub enum Error {
29    /// The path is not absolute.
30    NotAbsolute,
31    /// The path is not canonical.
32    NotCanonical,
33    /// The path contains a parent component that cannot be normalized away.
34    ContainsParent,
35}
36
37impl From<Infallible> for Error {
38    fn from(_: Infallible) -> Self {
39        unreachable!()
40    }
41}
42
43impl Display for Error {
44    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45        const PARENT_ERR: &str = if cfg!(windows) {
46            "path contains a '..' component that points outside of base directory"
47        } else {
48            "path contains a '..' component"
49        };
50
51        use Error::*;
52        match self {
53            NotAbsolute => f.write_str("path is not absolute"),
54            NotCanonical => f.write_str("path is not canonical"),
55            ContainsParent => f.write_str(PARENT_ERR),
56        }
57    }
58}
59
60impl std::error::Error for Error {}
61
62/// The error type indicating why a path cannot be converted into a normalized
63/// path.
64///
65/// This type is essentially an [`Error`] plus the original value.
66#[derive(Debug, Copy, Clone, PartialEq, Eq)]
67pub struct ConvertError<T> {
68    /// The error indicating why the conversion failed.
69    pub error: Error,
70    /// The value that failed to convert.
71    pub value: T,
72}
73
74impl<T> ConvertError<T> {
75    /// Creates a new `ConvertError` with the given error and value.
76    #[must_use]
77    #[inline]
78    pub const fn new(error: Error, value: T) -> Self {
79        Self { error, value }
80    }
81
82    /// Maps the value by applying the given function, without changing the
83    /// underlying error.
84    #[inline]
85    pub fn map<F, U>(self, f: F) -> ConvertError<U>
86    where
87        F: FnOnce(T) -> U,
88    {
89        ConvertError {
90            error: self.error,
91            value: f(self.value),
92        }
93    }
94}
95
96impl<T: ?Sized> ConvertError<&T> {
97    /// Clones the value, without changing the underlying error.
98    #[inline]
99    pub fn cloned(&self) -> ConvertError<T>
100    where
101        T: Clone,
102    {
103        ConvertError {
104            error: self.error,
105            value: self.value.clone(),
106        }
107    }
108
109    /// Converts the value into an owned version, without changing the
110    /// underlying error.
111    #[inline]
112    pub fn to_owned(&self) -> ConvertError<T::Owned>
113    where
114        T: ToOwned,
115    {
116        ConvertError {
117            error: self.error,
118            value: self.value.to_owned(),
119        }
120    }
121}
122
123impl<T> From<ConvertError<T>> for Error {
124    /// Converts a [`ConvertError`] into an [`Error`] by discarding the value.
125    #[inline]
126    fn from(value: ConvertError<T>) -> Self {
127        value.error
128    }
129}
130
131impl<T> From<Infallible> for ConvertError<T> {
132    fn from(_: Infallible) -> Self {
133        unreachable!()
134    }
135}
136
137impl<T: Debug> Display for ConvertError<T> {
138    #[inline]
139    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140        write!(f, "{}: {:?}", self.error, self.value)
141    }
142}
143
144impl<T: Debug> std::error::Error for ConvertError<T> {}
145
146/// A slice of a normalized path (akin to [`Path`]).
147///
148/// Since every normalized path is a path, this type implements [`Deref`] to
149/// [`Path`], and can be used wherever a `Path` is expected in most cases.
150///
151/// This type is `#[repr(transparent)]` over `Path`, as such, it is an *unsized*
152/// type as well, which has to be used behind a pointer like `&` or [`Box`]. For
153/// an owned version of this type, see [`NormpathBuf`].
154///
155/// Details about the normalization invariants can be found in the
156/// [crate documentation](crate).
157#[repr(transparent)]
158pub struct Normpath(pub(crate) Path);
159
160#[inline]
161pub(crate) unsafe fn cast_ref_unchecked(path: &Path) -> &Normpath {
162    // SAFETY: `#[repr(transparent)]` over Path.
163    unsafe { &*(ptr::from_ref(path) as *const _) }
164}
165
166#[inline]
167pub(crate) unsafe fn cast_box_unchecked(path: Box<Path>) -> Box<Normpath> {
168    // SAFETY: `#[repr(transparent)]` over Path.
169    unsafe { Box::from_raw(Box::into_raw(path) as *mut Normpath) }
170}
171
172#[inline]
173unsafe fn cast_arc_unchecked(path: Arc<Path>) -> Arc<Normpath> {
174    // SAFETY: `#[repr(transparent)]` over Path.
175    unsafe { Arc::from_raw(Arc::into_raw(path) as *const Normpath) }
176}
177
178#[inline]
179unsafe fn cast_rc_unchecked(path: Rc<Path>) -> Rc<Normpath> {
180    // SAFETY: `#[repr(transparent)]` over Path.
181    unsafe { Rc::from_raw(Rc::into_raw(path) as *const Normpath) }
182}
183
184#[inline]
185fn backcast_box(path: Box<Normpath>) -> Box<Path> {
186    // SAFETY: `#[repr(transparent)]` over Path.
187    unsafe { Box::from_raw(Box::into_raw(path) as *mut Path) }
188}
189
190impl AsRef<Path> for Normpath {
191    #[inline]
192    fn as_ref(&self) -> &Path {
193        &self.0
194    }
195}
196
197impl AsRef<Normpath> for Normpath {
198    #[inline]
199    fn as_ref(&self) -> &Normpath {
200        self
201    }
202}
203
204impl AsRef<OsStr> for Normpath {
205    #[inline]
206    fn as_ref(&self) -> &OsStr {
207        self.0.as_os_str()
208    }
209}
210
211impl Borrow<Path> for Normpath {
212    #[inline]
213    fn borrow(&self) -> &Path {
214        &self.0
215    }
216}
217
218impl Deref for Normpath {
219    type Target = Path;
220
221    #[inline]
222    fn deref(&self) -> &Self::Target {
223        &self.0
224    }
225}
226
227impl Clone for Box<Normpath> {
228    #[inline]
229    fn clone(&self) -> Self {
230        // SAFETY: `#[repr(transparent)]` over Path.
231        let path = unsafe { &*(ptr::from_ref(self) as *const Box<Path>) };
232
233        // SAFETY: the path is copied from a normalized path
234        unsafe { cast_box_unchecked(path.clone()) }
235    }
236}
237
238/// An owned normalized path that is mutable (akin to [`PathBuf`]).
239///
240/// This type provides methods like [`push`] that mutate the path in place,
241/// while maintaining the normalization invariants. It also implements
242/// [`Deref`] to [`Normpath`], meaning that all methods on [`Normpath`] slices
243/// are available as well.
244///
245/// Details about the normalization invariants can be found in the
246/// [crate documentation](crate).
247///
248/// [`push`]: Self::push
249#[derive(Clone)]
250pub struct NormpathBuf(pub(crate) PathBuf);
251
252impl AsRef<Normpath> for NormpathBuf {
253    #[inline]
254    fn as_ref(&self) -> &Normpath {
255        let p = self.0.as_path();
256
257        // SAFETY: `#[repr(transparent)]` over Path.
258        unsafe { cast_ref_unchecked(p) }
259    }
260}
261
262impl AsRef<Path> for NormpathBuf {
263    #[inline]
264    fn as_ref(&self) -> &Path {
265        &self.0
266    }
267}
268
269impl AsRef<OsStr> for NormpathBuf {
270    #[inline]
271    fn as_ref(&self) -> &OsStr {
272        self.0.as_os_str()
273    }
274}
275
276impl Borrow<Normpath> for NormpathBuf {
277    #[inline]
278    fn borrow(&self) -> &Normpath {
279        self.as_ref()
280    }
281}
282
283impl Borrow<Path> for NormpathBuf {
284    #[inline]
285    fn borrow(&self) -> &Path {
286        self.0.as_path()
287    }
288}
289
290impl Deref for NormpathBuf {
291    type Target = Normpath;
292
293    #[inline]
294    fn deref(&self) -> &Self::Target {
295        self.as_ref()
296    }
297}
298
299impl ToOwned for Normpath {
300    type Owned = NormpathBuf;
301
302    #[inline]
303    fn to_owned(&self) -> Self::Owned {
304        NormpathBuf(self.to_path_buf())
305    }
306}
307
308impl From<&Normpath> for NormpathBuf {
309    /// Creates a [`NormpathBuf`] from a [`Normpath`] slice.
310    ///
311    /// This will allocate and copy the data.
312    #[inline]
313    fn from(value: &Normpath) -> Self {
314        Self(value.to_path_buf())
315    }
316}
317
318impl From<&Normpath> for Box<Normpath> {
319    /// Creates a boxed [`Normpath`] from a reference.
320    ///
321    /// This will allocate and copy the data.
322    #[inline]
323    fn from(value: &Normpath) -> Self {
324        let value = Box::from(&value.0);
325        // SAFETY: the path is copied from a normalized path
326        unsafe { cast_box_unchecked(value) }
327    }
328}
329
330impl<'a> From<&'a Normpath> for Cow<'a, Normpath> {
331    /// Creates a clone-on-write pointer from a reference to [`Normpath`].
332    ///
333    /// This does not allocate or copy any data.
334    #[inline]
335    fn from(value: &'a Normpath) -> Self {
336        Cow::Borrowed(value)
337    }
338}
339
340impl From<&Normpath> for Arc<Normpath> {
341    /// Creates an [`Arc`] pointer from a reference to [`Normpath`].
342    ///
343    /// This will allocate and copy the data.
344    #[inline]
345    fn from(value: &Normpath) -> Self {
346        let value = Arc::<Path>::from(&value.0);
347        // SAFETY: the path is copied from a normalized path
348        unsafe { cast_arc_unchecked(value) }
349    }
350}
351
352impl From<&Normpath> for Rc<Normpath> {
353    /// Creates an [`Rc`] pointer from a reference to [`Normpath`].
354    ///
355    /// This will allocate and copy the data.
356    #[inline]
357    fn from(value: &Normpath) -> Self {
358        let value = Rc::<Path>::from(&value.0);
359        // SAFETY: the path is copied from a normalized path
360        unsafe { cast_rc_unchecked(value) }
361    }
362}
363
364impl From<Box<Normpath>> for NormpathBuf {
365    /// Creates a [`NormpathBuf`] from a boxed [`Normpath`].
366    ///
367    /// This does not allocate or copy any data.
368    #[inline]
369    fn from(value: Box<Normpath>) -> Self {
370        let value = backcast_box(value);
371        Self(value.into_path_buf())
372    }
373}
374
375impl From<Box<Normpath>> for PathBuf {
376    /// Creates a [`PathBuf`] from a boxed [`Normpath`].
377    ///
378    /// This does not allocate or copy any data.
379    #[inline]
380    fn from(value: Box<Normpath>) -> Self {
381        let value = backcast_box(value);
382        value.into_path_buf()
383    }
384}
385
386impl From<Box<Normpath>> for Box<Path> {
387    /// Creates a boxed [`Path`] from a boxed [`Normpath`].
388    ///
389    /// This is a cost-free conversion.
390    #[inline]
391    fn from(value: Box<Normpath>) -> Self {
392        backcast_box(value)
393    }
394}
395
396impl From<NormpathBuf> for Box<Normpath> {
397    /// Creates a boxed [`Normpath`] from a [`NormpathBuf`].
398    ///
399    /// This can allocate and copy the data depending on the implementation
400    /// of the standard library, but typically it will not.
401    #[inline]
402    fn from(value: NormpathBuf) -> Self {
403        let path = value.0.into_boxed_path();
404        // SAFETY: the path is normalized per se
405        unsafe { cast_box_unchecked(path) }
406    }
407}
408
409impl From<NormpathBuf> for Cow<'_, Normpath> {
410    /// Creates a clone-on-write pointer from a [`NormpathBuf`].
411    ///
412    /// This does not allocate or copy any data.
413    #[inline]
414    fn from(value: NormpathBuf) -> Self {
415        Cow::Owned(value)
416    }
417}
418
419impl From<NormpathBuf> for Arc<Normpath> {
420    /// Creates an [`Arc`] pointer from a [`NormpathBuf`].
421    ///
422    /// This will allocate and copy the data.
423    #[inline]
424    fn from(value: NormpathBuf) -> Self {
425        let path = Arc::<Path>::from(value.0);
426        // SAFETY: the path is normalized per se
427        unsafe { cast_arc_unchecked(path) }
428    }
429}
430
431impl From<NormpathBuf> for Rc<Normpath> {
432    /// Creates an [`Rc`] pointer from a [`NormpathBuf`].
433    ///
434    /// This will allocate and copy the data.
435    #[inline]
436    fn from(value: NormpathBuf) -> Self {
437        let path = Rc::<Path>::from(value.0);
438        // SAFETY: the path is normalized per se
439        unsafe { cast_rc_unchecked(path) }
440    }
441}
442
443impl From<NormpathBuf> for PathBuf {
444    /// Creates a [`PathBuf`] from a [`NormpathBuf`].
445    ///
446    /// This is a cost-free conversion.
447    #[inline]
448    fn from(value: NormpathBuf) -> Self {
449        value.0
450    }
451}
452
453impl From<NormpathBuf> for OsString {
454    /// Creates an [`OsString`] from a [`NormpathBuf`].
455    ///
456    /// This is a cost-free conversion.
457    #[inline]
458    fn from(value: NormpathBuf) -> Self {
459        value.0.into_os_string()
460    }
461}
462
463impl<'a> From<&'a NormpathBuf> for Cow<'a, Normpath> {
464    /// Creates a clone-on-write pointer from a reference to [`NormpathBuf`].
465    ///
466    /// This does not allocate or copy any data.
467    #[inline]
468    fn from(value: &'a NormpathBuf) -> Self {
469        Cow::Borrowed(value.as_ref())
470    }
471}
472
473impl From<&NormpathBuf> for NormpathBuf {
474    /// Clones a [`NormpathBuf`] from a reference to [`NormpathBuf`].
475    #[inline]
476    fn from(value: &NormpathBuf) -> Self {
477        value.clone()
478    }
479}
480
481impl From<Cow<'_, Normpath>> for NormpathBuf {
482    /// Creates a [`NormpathBuf`] from a clone-on-write pointer to [`Normpath`].
483    ///
484    /// Converting from [`Cow::Owned`] does not allocate or copy any data.
485    #[inline]
486    fn from(value: Cow<'_, Normpath>) -> Self {
487        use Cow::*;
488        match value {
489            Borrowed(value) => Self(value.to_path_buf()),
490            Owned(value) => value,
491        }
492    }
493}
494
495impl<'a> From<Cow<'a, Normpath>> for Box<Normpath> {
496    /// Creates a boxed [`Normpath`] from a clone-on-write pointer to
497    /// [`Normpath`].
498    ///
499    /// Converting from [`Cow::Owned`] can allocate and copy the data depending
500    /// on the implementation of the standard library, but typically it will
501    /// not.
502    #[inline]
503    fn from(value: Cow<'a, Normpath>) -> Self {
504        let path = match value {
505            Cow::Borrowed(value) => Box::from(&value.0),
506            Cow::Owned(value) => value.0.into_boxed_path(),
507        };
508
509        // SAFETY: the path is either copied from a normalized path, or
510        // normalized per se
511        unsafe { cast_box_unchecked(path) }
512    }
513}
514
515impl PartialEq for Normpath {
516    #[inline]
517    fn eq(&self, other: &Self) -> bool {
518        self.0.as_os_str() == other.0.as_os_str()
519    }
520}
521
522impl Eq for Normpath {}
523
524impl Hash for Normpath {
525    #[inline]
526    fn hash<H: Hasher>(&self, state: &mut H) {
527        self.0.as_os_str().hash(state);
528    }
529}
530
531impl Ord for Normpath {
532    #[inline]
533    fn cmp(&self, other: &Self) -> cmp::Ordering {
534        self.0.as_os_str().cmp(other.0.as_os_str())
535    }
536}
537
538impl PartialOrd for Normpath {
539    #[inline]
540    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
541        Some(self.cmp(other))
542    }
543}
544
545impl PartialEq for NormpathBuf {
546    #[inline]
547    fn eq(&self, other: &Self) -> bool {
548        self.0.as_os_str() == other.0.as_os_str()
549    }
550}
551
552impl Eq for NormpathBuf {}
553
554impl Hash for NormpathBuf {
555    #[inline]
556    fn hash<H: Hasher>(&self, state: &mut H) {
557        self.0.as_os_str().hash(state);
558    }
559}
560
561impl Ord for NormpathBuf {
562    #[inline]
563    fn cmp(&self, other: &Self) -> cmp::Ordering {
564        self.0.as_os_str().cmp(other.0.as_os_str())
565    }
566}
567
568impl PartialOrd for NormpathBuf {
569    #[inline]
570    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
571        Some(self.cmp(other))
572    }
573}
574
575impl PartialEq<Normpath> for NormpathBuf {
576    #[inline]
577    fn eq(&self, other: &Normpath) -> bool {
578        self.0.as_os_str() == other.0.as_os_str()
579    }
580}
581
582impl PartialEq<NormpathBuf> for Normpath {
583    #[inline]
584    fn eq(&self, other: &NormpathBuf) -> bool {
585        self.0.as_os_str() == other.0.as_os_str()
586    }
587}
588
589impl PartialOrd<Normpath> for NormpathBuf {
590    #[inline]
591    fn partial_cmp(&self, other: &Normpath) -> Option<cmp::Ordering> {
592        Some(self.0.as_os_str().cmp(other.0.as_os_str()))
593    }
594}
595
596impl PartialOrd<NormpathBuf> for Normpath {
597    #[inline]
598    fn partial_cmp(&self, other: &NormpathBuf) -> Option<cmp::Ordering> {
599        Some(self.0.as_os_str().cmp(other.0.as_os_str()))
600    }
601}
602
603macro_rules! impl_cmp {
604    (<$($life:lifetime),*> $lhs:ty, $rhs:ty) => {
605        impl<$($life),*> PartialEq<$rhs> for $lhs {
606            #[inline]
607            fn eq(&self, other: &$rhs) -> bool {
608                <Path as PartialEq>::eq(self.as_ref(), other.as_ref())
609            }
610        }
611
612        impl<$($life),*> PartialEq<$lhs> for $rhs {
613            #[inline]
614            fn eq(&self, other: &$lhs) -> bool {
615                <Path as PartialEq>::eq(other.as_ref(), self.as_ref())
616            }
617        }
618
619        impl<$($life),*> PartialOrd<$rhs> for $lhs {
620            #[inline]
621            fn partial_cmp(&self, other: &$rhs) -> Option<std::cmp::Ordering> {
622                <Path as PartialOrd>::partial_cmp(self.as_ref(), other.as_ref())
623            }
624        }
625
626        impl<$($life),*> PartialOrd<$lhs> for $rhs {
627            #[inline]
628            fn partial_cmp(&self, other: &$lhs) -> Option<std::cmp::Ordering> {
629                <Path as PartialOrd>::partial_cmp(other.as_ref(), self.as_ref())
630            }
631        }
632    };
633}
634
635impl_cmp!(<> Normpath, Path);
636impl_cmp!(<'a> Normpath, &'a Path);
637impl_cmp!(<> Normpath, PathBuf);
638impl_cmp!(<'a> Normpath, Cow<'a, Path>);
639impl_cmp!(<> Normpath, OsStr);
640impl_cmp!(<'a> Normpath, &'a OsStr);
641impl_cmp!(<> Normpath, OsString);
642impl_cmp!(<'a> Normpath, Cow<'a, OsStr>);
643
644impl_cmp!(<> NormpathBuf, Path);
645impl_cmp!(<'a> NormpathBuf, &'a Path);
646impl_cmp!(<> NormpathBuf, PathBuf);
647impl_cmp!(<'a> NormpathBuf, Cow<'a, Path>);
648impl_cmp!(<> NormpathBuf, OsStr);
649impl_cmp!(<'a> NormpathBuf, &'a OsStr);
650impl_cmp!(<> NormpathBuf, OsString);
651impl_cmp!(<'a> NormpathBuf, Cow<'a, OsStr>);
652
653impl_cmp!(<'a> &'a Normpath, Path);
654impl_cmp!(<'a> &'a Normpath, PathBuf);
655impl_cmp!(<'a, 'b> &'a Normpath, Cow<'b, Path>);
656impl_cmp!(<'a> &'a Normpath, OsStr);
657impl_cmp!(<'a> &'a Normpath, OsString);
658impl_cmp!(<'a, 'b> &'a Normpath, Cow<'b, OsStr>);
659
660macro_rules! impl_eq_literal {
661    ($lhs:ty, $rhs:ty) => {
662        impl PartialEq<$rhs> for $lhs {
663            #[inline]
664            fn eq(&self, other: &$rhs) -> bool {
665                <OsStr as PartialEq>::eq(self.as_ref(), other.as_ref())
666            }
667        }
668
669        impl PartialEq<$lhs> for $rhs {
670            #[inline]
671            fn eq(&self, other: &$lhs) -> bool {
672                <OsStr as PartialEq>::eq(other.as_ref(), self.as_ref())
673            }
674        }
675    };
676}
677
678impl_eq_literal!(Normpath, str);
679impl_eq_literal!(NormpathBuf, str);
680impl_eq_literal!(Normpath, String);
681impl_eq_literal!(NormpathBuf, String);
682
683impl Debug for Normpath {
684    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
685        Debug::fmt(&self.0, f)
686    }
687}
688
689impl Debug for NormpathBuf {
690    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
691        Debug::fmt(&self.0, f)
692    }
693}