Skip to main content

normpath/
base.rs

1use std::borrow::Borrow;
2use std::borrow::Cow;
3use std::cmp::Ordering;
4use std::ffi::OsStr;
5use std::ffi::OsString;
6use std::fs::Metadata;
7use std::fs::ReadDir;
8use std::hash::Hash;
9use std::hash::Hasher;
10use std::io;
11use std::mem;
12use std::ops::Deref;
13use std::path::Component;
14use std::path::Components;
15use std::path::Path;
16use std::path::PathBuf;
17
18use super::error::MissingPrefixBufError;
19use super::error::MissingPrefixError;
20use super::error::ParentError;
21use super::imp;
22use super::PathExt;
23
24fn cow_path_into_base_path(path: Cow<'_, Path>) -> Cow<'_, BasePath> {
25    debug_assert!(imp::is_base(&path));
26
27    match path {
28        Cow::Borrowed(path) => {
29            Cow::Borrowed(BasePath::from_inner(path.as_os_str()))
30        }
31        Cow::Owned(path) => Cow::Owned(BasePathBuf(path)),
32    }
33}
34
35/// A borrowed path that has a [prefix] on Windows.
36///
37/// Note that comparison traits such as [`PartialEq`] will compare paths
38/// literally instead of comparing components. The former is more efficient and
39/// easier to use correctly.
40///
41/// # Safety
42///
43/// This type should not be used for memory safety, but implementations can
44/// panic if this path is missing a prefix on Windows. A safe `new_unchecked`
45/// method might be added later that can safely create invalid base paths.
46///
47/// [prefix]: ::std::path::Prefix
48#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
49#[repr(transparent)]
50pub struct BasePath(pub(super) OsStr);
51
52impl BasePath {
53    pub(super) fn from_inner(path: &OsStr) -> &Self {
54        // SAFETY: This struct has a layout that makes this operation safe.
55        unsafe { mem::transmute(path) }
56    }
57
58    /// Creates a new base path.
59    ///
60    /// On Windows, if `path` is missing a [prefix], it will be joined to the
61    /// current directory.
62    ///
63    /// # Errors
64    ///
65    /// Returns an error if reading the current directory fails.
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// # use std::io;
71    /// use std::path::Path;
72    ///
73    /// use normpath::BasePath;
74    ///
75    /// if cfg!(windows) {
76    ///     let path = Path::new(r"X:\foo\bar");
77    ///     assert_eq!(path, *BasePath::new(path)?);
78    ///
79    ///     assert!(BasePath::new(Path::new(r"foo\bar")).is_ok());
80    /// }
81    /// #
82    /// # Ok::<_, io::Error>(())
83    /// ```
84    ///
85    /// [prefix]: ::std::path::Prefix
86    #[inline]
87    pub fn new<'a, P>(path: P) -> io::Result<Cow<'a, Self>>
88    where
89        P: Into<Cow<'a, Path>>,
90    {
91        let path = path.into();
92        match path {
93            Cow::Borrowed(path) => Self::try_new(path)
94                .map(Cow::Borrowed)
95                .or_else(|_| imp::to_base(path).map(Cow::Owned)),
96            Cow::Owned(path) => BasePathBuf::new(path).map(Cow::Owned),
97        }
98    }
99
100    /// Creates a new base path.
101    ///
102    /// # Errors
103    ///
104    /// On Windows, returns an error if `path` is missing a [prefix].
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// use std::path::Path;
110    ///
111    /// # use normpath::error::MissingPrefixError;
112    /// use normpath::BasePath;
113    ///
114    /// if cfg!(windows) {
115    ///     let path = r"X:\foo\bar";
116    ///     assert_eq!(Path::new(path), BasePath::try_new(path)?);
117    ///
118    ///     assert!(BasePath::try_new(r"foo\bar").is_err());
119    /// }
120    /// #
121    /// # Ok::<_, MissingPrefixError>(())
122    /// ```
123    ///
124    /// [prefix]: ::std::path::Prefix
125    #[inline]
126    pub fn try_new<P>(path: &P) -> Result<&Self, MissingPrefixError>
127    where
128        P: AsRef<Path> + ?Sized,
129    {
130        let path = path.as_ref();
131        if imp::is_base(path) {
132            Ok(Self::from_inner(path.as_os_str()))
133        } else {
134            Err(MissingPrefixError(()))
135        }
136    }
137
138    /// Returns a reference to the wrapped path as a platform string.
139    #[inline]
140    #[must_use]
141    pub fn as_os_str(&self) -> &OsStr {
142        &self.0
143    }
144
145    /// Returns a reference to the wrapped path.
146    #[inline]
147    #[must_use]
148    pub fn as_path(&self) -> &Path {
149        Path::new(&self.0)
150    }
151
152    /// Equivalent to [`Path::canonicalize`].
153    #[inline]
154    pub fn canonicalize(&self) -> io::Result<BasePathBuf> {
155        self.as_path().canonicalize().map(|base| {
156            debug_assert!(imp::is_base(&base));
157            BasePathBuf(base)
158        })
159    }
160
161    /// Equivalent to [`Path::components`].
162    #[inline]
163    pub fn components(&self) -> Components<'_> {
164        self.as_path().components()
165    }
166
167    /// Equivalent to [`Path::ends_with`].
168    #[inline]
169    #[must_use]
170    pub fn ends_with<P>(&self, child: P) -> bool
171    where
172        P: AsRef<Path>,
173    {
174        self.as_path().ends_with(child)
175    }
176
177    /// Equivalent to [`Path::exists`].
178    #[inline]
179    #[must_use]
180    pub fn exists(&self) -> bool {
181        self.as_path().exists()
182    }
183
184    /// Equivalent to [`PathExt::expand`].
185    #[inline]
186    pub fn expand(&self) -> io::Result<Cow<'_, Self>> {
187        self.as_path().expand().map(cow_path_into_base_path)
188    }
189
190    /// Equivalent to [`Path::extension`].
191    #[inline]
192    #[must_use]
193    pub fn extension(&self) -> Option<&OsStr> {
194        self.as_path().extension()
195    }
196
197    /// Equivalent to [`Path::file_name`].
198    #[inline]
199    #[must_use]
200    pub fn file_name(&self) -> Option<&OsStr> {
201        self.as_path().file_name()
202    }
203
204    /// Equivalent to [`Path::file_stem`].
205    #[inline]
206    #[must_use]
207    pub fn file_stem(&self) -> Option<&OsStr> {
208        self.as_path().file_stem()
209    }
210
211    /// Equivalent to [`Path::has_root`].
212    #[inline]
213    #[must_use]
214    pub fn has_root(&self) -> bool {
215        self.as_path().has_root()
216    }
217
218    /// Equivalent to [`Path::is_absolute`].
219    #[inline]
220    #[must_use]
221    pub fn is_absolute(&self) -> bool {
222        self.as_path().is_absolute()
223    }
224
225    /// Equivalent to [`Path::is_dir`].
226    #[inline]
227    #[must_use]
228    pub fn is_dir(&self) -> bool {
229        self.as_path().is_dir()
230    }
231
232    /// Equivalent to [`Path::is_file`].
233    #[inline]
234    #[must_use]
235    pub fn is_file(&self) -> bool {
236        self.as_path().is_file()
237    }
238
239    /// Equivalent to [`Path::is_relative`].
240    #[inline]
241    #[must_use]
242    pub fn is_relative(&self) -> bool {
243        self.as_path().is_relative()
244    }
245
246    /// Equivalent to [`Path::is_symlink`].
247    #[inline]
248    #[must_use]
249    pub fn is_symlink(&self) -> bool {
250        self.as_path().is_symlink()
251    }
252
253    /// An improved version of [`Path::join`] that handles more edge cases.
254    ///
255    /// For example, on Windows, leading `.` and `..` components of `path` will
256    /// be normalized if possible. If `self` is a [verbatim] path, it would be
257    /// invalid to normalize them later.
258    ///
259    /// You should still call [`normalize`] before [`parent`] to normalize some
260    /// additional components.
261    ///
262    /// # Examples
263    ///
264    /// ```
265    /// use std::path::Path;
266    ///
267    /// use normpath::BasePath;
268    ///
269    /// if cfg!(windows) {
270    ///     assert_eq!(
271    ///         Path::new(r"\\?\foo\baz\test.rs"),
272    ///         BasePath::try_new(r"\\?\foo\bar")
273    ///             .unwrap()
274    ///             .join("../baz/test.rs"),
275    ///     );
276    /// }
277    /// ```
278    ///
279    /// [`normalize`]: Self::normalize
280    /// [`parent`]: Self::parent
281    /// [verbatim]: ::std::path::Prefix::is_verbatim
282    #[inline]
283    pub fn join<P>(&self, path: P) -> BasePathBuf
284    where
285        P: AsRef<Path>,
286    {
287        let mut base = self.to_owned();
288        base.push(path);
289        base
290    }
291
292    /// Equivalent to [`PathExt::localize_name`].
293    #[cfg(feature = "localization")]
294    #[cfg_attr(normpath_docs_rs, doc(cfg(feature = "localization")))]
295    #[inline]
296    #[must_use]
297    pub fn localize_name(&self) -> Cow<'_, OsStr> {
298        self.as_path().localize_name()
299    }
300
301    /// Equivalent to [`Path::metadata`].
302    #[inline]
303    pub fn metadata(&self) -> io::Result<Metadata> {
304        self.as_path().metadata()
305    }
306
307    /// Equivalent to [`PathExt::normalize`].
308    #[inline]
309    pub fn normalize(&self) -> io::Result<BasePathBuf> {
310        self.as_path().normalize()
311    }
312
313    /// Equivalent to [`PathExt::normalize_virtually`].
314    #[cfg(any(doc, windows))]
315    #[cfg_attr(normpath_docs_rs, doc(cfg(windows)))]
316    #[inline]
317    pub fn normalize_virtually(&self) -> io::Result<BasePathBuf> {
318        self.as_path().normalize_virtually()
319    }
320
321    fn check_parent(&self) -> Result<(), ParentError> {
322        self.components()
323            .next_back()
324            .filter(|x| matches!(x, Component::Normal(_) | Component::RootDir))
325            .map(|_| ())
326            .ok_or(ParentError(()))
327    }
328
329    /// Returns this path without its last component.
330    ///
331    /// Returns `Ok(None)` if the last component is [`Component::RootDir`].
332    ///
333    /// You should usually only call this method on [normalized] paths. They
334    /// will prevent an unexpected path from being returned due to symlinks,
335    /// and some `.` and `..` components will be normalized.
336    ///
337    /// # Errors
338    ///
339    /// Returns an error if the last component is not [`Component::Normal`] or
340    /// [`Component::RootDir`]. To ignore this error, use [`parent_unchecked`].
341    ///
342    /// # Examples
343    ///
344    /// ```
345    /// use std::path::Path;
346    ///
347    /// # use normpath::error::ParentError;
348    /// use normpath::BasePath;
349    ///
350    /// if cfg!(windows) {
351    ///     assert_eq!(
352    ///         Path::new(r"X:\foo"),
353    ///         BasePath::try_new(r"X:\foo\bar").unwrap().parent()?.unwrap(),
354    ///     );
355    /// }
356    /// #
357    /// # Ok::<_, ParentError>(())
358    /// ```
359    ///
360    /// [normalized]: Self::normalize
361    /// [`parent_unchecked`]: Self::parent_unchecked
362    #[inline]
363    pub fn parent(&self) -> Result<Option<&Self>, ParentError> {
364        self.check_parent().map(|()| self.parent_unchecked())
365    }
366
367    /// Equivalent to [`Path::parent`].
368    ///
369    /// It is usually better to use [`parent`].
370    ///
371    /// # Examples
372    ///
373    /// ```
374    /// use std::path::Path;
375    ///
376    /// use normpath::BasePath;
377    ///
378    /// if cfg!(windows) {
379    ///     assert_eq!(
380    ///         Path::new(r"X:\foo"),
381    ///         BasePath::try_new(r"X:\foo\..")
382    ///             .unwrap()
383    ///             .parent_unchecked()
384    ///             .unwrap(),
385    ///     );
386    /// }
387    /// ```
388    ///
389    /// [`parent`]: Self::parent
390    #[inline]
391    #[must_use]
392    pub fn parent_unchecked(&self) -> Option<&Self> {
393        self.as_path()
394            .parent()
395            .map(|x| Self::from_inner(x.as_os_str()))
396    }
397
398    /// Equivalent to [`Path::read_dir`].
399    #[inline]
400    pub fn read_dir(&self) -> io::Result<ReadDir> {
401        self.as_path().read_dir()
402    }
403
404    /// Equivalent to [`Path::read_link`].
405    #[inline]
406    pub fn read_link(&self) -> io::Result<PathBuf> {
407        self.as_path().read_link()
408    }
409
410    /// Equivalent to [`PathExt::shorten`].
411    #[inline]
412    pub fn shorten(&self) -> io::Result<Cow<'_, Self>> {
413        self.as_path().shorten().map(cow_path_into_base_path)
414    }
415
416    /// Equivalent to [`Path::starts_with`].
417    #[inline]
418    #[must_use]
419    pub fn starts_with<P>(&self, base: P) -> bool
420    where
421        P: AsRef<Path>,
422    {
423        self.as_path().starts_with(base)
424    }
425
426    /// Equivalent to [`Path::symlink_metadata`].
427    #[inline]
428    pub fn symlink_metadata(&self) -> io::Result<Metadata> {
429        self.as_path().symlink_metadata()
430    }
431
432    /// Equivalent to [`Path::try_exists`].
433    #[inline]
434    pub fn try_exists(&self) -> io::Result<bool> {
435        self.as_path().try_exists()
436    }
437}
438
439impl AsRef<OsStr> for BasePath {
440    #[inline]
441    fn as_ref(&self) -> &OsStr {
442        &self.0
443    }
444}
445
446impl AsRef<Path> for BasePath {
447    #[inline]
448    fn as_ref(&self) -> &Path {
449        self.as_path()
450    }
451}
452
453impl AsRef<Self> for BasePath {
454    #[inline]
455    fn as_ref(&self) -> &Self {
456        self
457    }
458}
459
460impl<'a> From<&'a BasePath> for Cow<'a, BasePath> {
461    #[inline]
462    fn from(value: &'a BasePath) -> Self {
463        Cow::Borrowed(value)
464    }
465}
466
467impl PartialEq<Path> for BasePath {
468    #[inline]
469    fn eq(&self, other: &Path) -> bool {
470        &self.0 == other.as_os_str()
471    }
472}
473
474impl PartialEq<BasePath> for Path {
475    #[inline]
476    fn eq(&self, other: &BasePath) -> bool {
477        other == self
478    }
479}
480
481impl PartialOrd<Path> for BasePath {
482    #[inline]
483    fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
484        self.0.partial_cmp(other.as_os_str())
485    }
486}
487
488impl PartialOrd<BasePath> for Path {
489    #[inline]
490    fn partial_cmp(&self, other: &BasePath) -> Option<Ordering> {
491        other.partial_cmp(self)
492    }
493}
494
495impl ToOwned for BasePath {
496    type Owned = BasePathBuf;
497
498    #[inline]
499    fn to_owned(&self) -> Self::Owned {
500        BasePathBuf(self.0.to_owned().into())
501    }
502}
503
504/// An owned path that has a [prefix] on Windows.
505///
506/// For more information, see [`BasePath`].
507///
508/// [prefix]: ::std::path::Prefix
509#[derive(Clone, Debug)]
510pub struct BasePathBuf(pub(super) PathBuf);
511
512impl BasePathBuf {
513    /// Equivalent to [`BasePath::new`] but returns an owned path.
514    ///
515    /// # Examples
516    ///
517    /// ```
518    /// # use std::io;
519    /// use std::path::Path;
520    ///
521    /// use normpath::BasePathBuf;
522    ///
523    /// if cfg!(windows) {
524    ///     let path = r"X:\foo\bar";
525    ///     assert_eq!(Path::new(path), BasePathBuf::new(path)?);
526    ///
527    ///     assert!(BasePathBuf::new(r"foo\bar").is_ok());
528    /// }
529    /// #
530    /// # Ok::<_, io::Error>(())
531    /// ```
532    #[inline]
533    pub fn new<P>(path: P) -> io::Result<Self>
534    where
535        P: Into<PathBuf>,
536    {
537        Self::try_new(path).or_else(|x| imp::to_base(&x.0))
538    }
539
540    /// Equivalent to [`BasePath::try_new`] but returns an owned path.
541    ///
542    /// # Examples
543    ///
544    /// ```
545    /// use std::path::Path;
546    ///
547    /// # use normpath::error::MissingPrefixBufError;
548    /// use normpath::BasePathBuf;
549    ///
550    /// if cfg!(windows) {
551    ///     let path = r"X:\foo\bar";
552    ///     assert_eq!(Path::new(path), BasePathBuf::try_new(path)?);
553    ///
554    ///     assert!(BasePathBuf::try_new(r"foo\bar").is_err());
555    /// }
556    /// #
557    /// # Ok::<_, MissingPrefixBufError>(())
558    /// ```
559    #[inline]
560    pub fn try_new<P>(path: P) -> Result<Self, MissingPrefixBufError>
561    where
562        P: Into<PathBuf>,
563    {
564        let path = path.into();
565        if imp::is_base(&path) {
566            Ok(Self(path))
567        } else {
568            Err(MissingPrefixBufError(path))
569        }
570    }
571
572    /// Returns the wrapped path as a platform string.
573    #[inline]
574    #[must_use]
575    pub fn into_os_string(self) -> OsString {
576        self.0.into_os_string()
577    }
578
579    /// Returns the wrapped path.
580    #[inline]
581    #[must_use]
582    pub fn into_path_buf(self) -> PathBuf {
583        self.0
584    }
585
586    /// Equivalent to [`BasePath::parent`] but modifies `self` in place.
587    ///
588    /// Returns `Ok(false)` when [`BasePath::parent`] returns `Ok(None)`.
589    ///
590    /// # Examples
591    ///
592    /// ```
593    /// use std::path::Path;
594    ///
595    /// # use normpath::error::ParentError;
596    /// use normpath::BasePathBuf;
597    ///
598    /// if cfg!(windows) {
599    ///     let mut path = BasePathBuf::try_new(r"X:\foo\bar").unwrap();
600    ///     assert!(path.pop()?);
601    ///     assert_eq!(Path::new(r"X:\foo"), path);
602    /// }
603    /// #
604    /// # Ok::<_, ParentError>(())
605    /// ```
606    #[inline]
607    pub fn pop(&mut self) -> Result<bool, ParentError> {
608        self.check_parent().map(|()| self.pop_unchecked())
609    }
610
611    /// Equivalent to [`PathBuf::pop`].
612    ///
613    /// It is usually better to use [`pop`].
614    ///
615    /// # Examples
616    ///
617    /// ```
618    /// use std::path::Path;
619    ///
620    /// use normpath::BasePathBuf;
621    ///
622    /// if cfg!(windows) {
623    ///     let mut path = BasePathBuf::try_new(r"X:\foo\..").unwrap();
624    ///     assert!(path.pop_unchecked());
625    ///     assert_eq!(Path::new(r"X:\foo"), path);
626    /// }
627    /// ```
628    ///
629    /// [`pop`]: Self::pop
630    #[inline]
631    pub fn pop_unchecked(&mut self) -> bool {
632        self.0.pop()
633    }
634
635    /// Equivalent to [`BasePath::join`] but modifies `self` in place.
636    ///
637    /// # Examples
638    ///
639    /// ```
640    /// use std::path::Path;
641    ///
642    /// use normpath::BasePathBuf;
643    ///
644    /// if cfg!(windows) {
645    ///     let mut path = BasePathBuf::try_new(r"\\?\foo\bar").unwrap();
646    ///     path.push("../baz/test.rs");
647    ///     assert_eq!(Path::new(r"\\?\foo\baz\test.rs"), path);
648    /// }
649    /// ```
650    #[inline]
651    pub fn push<P>(&mut self, path: P)
652    where
653        P: AsRef<Path>,
654    {
655        imp::push(self, path.as_ref());
656    }
657}
658
659impl AsRef<OsStr> for BasePathBuf {
660    #[inline]
661    fn as_ref(&self) -> &OsStr {
662        self.as_os_str()
663    }
664}
665
666impl AsRef<Path> for BasePathBuf {
667    #[inline]
668    fn as_ref(&self) -> &Path {
669        &self.0
670    }
671}
672
673impl AsRef<BasePath> for BasePathBuf {
674    #[inline]
675    fn as_ref(&self) -> &BasePath {
676        self
677    }
678}
679
680impl Borrow<BasePath> for BasePathBuf {
681    #[inline]
682    fn borrow(&self) -> &BasePath {
683        self
684    }
685}
686
687impl Deref for BasePathBuf {
688    type Target = BasePath;
689
690    #[inline]
691    fn deref(&self) -> &BasePath {
692        BasePath::from_inner(self.0.as_os_str())
693    }
694}
695
696impl Eq for BasePathBuf {}
697
698impl From<BasePathBuf> for Cow<'_, BasePath> {
699    #[inline]
700    fn from(value: BasePathBuf) -> Self {
701        Cow::Owned(value)
702    }
703}
704
705impl From<BasePathBuf> for OsString {
706    #[inline]
707    fn from(value: BasePathBuf) -> Self {
708        value.into_os_string()
709    }
710}
711
712impl From<BasePathBuf> for PathBuf {
713    #[inline]
714    fn from(value: BasePathBuf) -> Self {
715        value.0
716    }
717}
718
719impl Hash for BasePathBuf {
720    #[inline]
721    fn hash<H>(&self, state: &mut H)
722    where
723        H: Hasher,
724    {
725        (**self).hash(state);
726    }
727}
728
729impl Ord for BasePathBuf {
730    #[inline]
731    fn cmp(&self, other: &Self) -> Ordering {
732        (**self).cmp(&**other)
733    }
734}
735
736impl PartialEq for BasePathBuf {
737    #[inline]
738    fn eq(&self, other: &Self) -> bool {
739        **self == **other
740    }
741}
742
743#[expect(clippy::non_canonical_partial_ord_impl)]
744impl PartialOrd for BasePathBuf {
745    #[inline]
746    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
747        (**self).partial_cmp(&**other)
748    }
749}
750
751#[cfg(feature = "print_bytes")]
752#[cfg_attr(normpath_docs_rs, doc(cfg(feature = "print_bytes")))]
753mod print_bytes {
754    use print_bytes::ByteStr;
755    use print_bytes::ToBytes;
756    #[cfg(windows)]
757    use print_bytes::WideStr;
758
759    use super::BasePath;
760    use super::BasePathBuf;
761
762    impl ToBytes for BasePath {
763        #[inline]
764        fn to_bytes(&self) -> ByteStr<'_> {
765            self.0.to_bytes()
766        }
767
768        #[cfg(windows)]
769        #[inline]
770        fn to_wide(&self) -> Option<WideStr> {
771            self.0.to_wide()
772        }
773    }
774
775    impl ToBytes for BasePathBuf {
776        #[inline]
777        fn to_bytes(&self) -> ByteStr<'_> {
778            (**self).to_bytes()
779        }
780
781        #[cfg(windows)]
782        #[inline]
783        fn to_wide(&self) -> Option<WideStr> {
784            (**self).to_wide()
785        }
786    }
787}
788
789#[cfg(feature = "serde")]
790#[cfg_attr(normpath_docs_rs, doc(cfg(feature = "serde")))]
791mod serde {
792    use std::ffi::OsString;
793
794    use serde::Deserialize;
795    use serde::Deserializer;
796    use serde::Serialize;
797    use serde::Serializer;
798
799    use super::BasePath;
800    use super::BasePathBuf;
801
802    impl<'de> Deserialize<'de> for BasePathBuf {
803        #[inline]
804        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
805        where
806            D: Deserializer<'de>,
807        {
808            OsString::deserialize(deserializer).map(|x| Self(x.into()))
809        }
810    }
811
812    impl Serialize for BasePath {
813        #[inline]
814        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
815        where
816            S: Serializer,
817        {
818            serializer.serialize_newtype_struct("BasePath", &self.0)
819        }
820    }
821
822    impl Serialize for BasePathBuf {
823        #[inline]
824        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
825        where
826            S: Serializer,
827        {
828            serializer
829                .serialize_newtype_struct("BasePathBuf", self.as_os_str())
830        }
831    }
832}
833
834#[cfg(feature = "uniquote")]
835#[cfg_attr(normpath_docs_rs, doc(cfg(feature = "uniquote")))]
836mod uniquote {
837    use uniquote::Formatter;
838    use uniquote::Quote;
839    use uniquote::Result;
840
841    use super::BasePath;
842    use super::BasePathBuf;
843
844    impl Quote for BasePath {
845        #[inline]
846        fn escape(&self, f: &mut Formatter<'_>) -> Result {
847            self.0.escape(f)
848        }
849    }
850
851    impl Quote for BasePathBuf {
852        #[inline]
853        fn escape(&self, f: &mut Formatter<'_>) -> Result {
854            (**self).escape(f)
855        }
856    }
857}