Skip to main content

typed_path/common/utf8/
path.rs

1use alloc::borrow::{Cow, ToOwned};
2use alloc::rc::Rc;
3#[cfg(target_has_atomic = "ptr")]
4use alloc::sync::Arc;
5use core::hash::{Hash, Hasher};
6use core::marker::PhantomData;
7use core::str::Utf8Error;
8use core::{cmp, fmt};
9
10#[cfg(feature = "serde")]
11use serde_core::{
12    de,
13    de::{Unexpected, Visitor},
14    Deserialize, Deserializer, Serialize, Serializer,
15};
16
17use crate::no_std_compat::*;
18use crate::{
19    CheckedPathError, Encoding, Path, StripPrefixError, Utf8Ancestors, Utf8Component,
20    Utf8Components, Utf8Encoding, Utf8Iter, Utf8PathBuf,
21};
22
23/// A slice of a path (akin to [`str`]).
24///
25/// This type supports a number of operations for inspecting a path, including
26/// breaking the path into its components (separated by `/` on Unix and by either
27/// `/` or `\` on Windows), extracting the file name, determining whether the path
28/// is absolute, and so on.
29///
30/// This is an *unsized* type, meaning that it must always be used behind a
31/// pointer like `&` or [`Box`]. For an owned version of this type,
32/// see [`Utf8PathBuf`].
33///
34/// # Examples
35///
36/// ```
37/// use typed_path::{Utf8Path, Utf8UnixEncoding};
38///
39/// // NOTE: A path cannot be created on its own without a defined encoding,
40/// //       but all encodings work on all operating systems, providing the
41/// //       ability to parse and operate on paths independently of the
42/// //       compiled platform
43/// let path = Utf8Path::<Utf8UnixEncoding>::new("./foo/bar.txt");
44///
45/// let parent = path.parent();
46/// assert_eq!(parent, Some(Utf8Path::new("./foo")));
47///
48/// let file_stem = path.file_stem();
49/// assert_eq!(file_stem, Some("bar"));
50///
51/// let extension = path.extension();
52/// assert_eq!(extension, Some("txt"));
53/// ```
54///
55/// In addition to explicitly using [`Utf8Encoding`]s, you can also
56/// leverage aliases available from the crate to work with paths:
57///
58/// ```
59/// use typed_path::{Utf8UnixPath, Utf8WindowsPath};
60///
61/// // Same as Utf8Path<Utf8UnixEncoding>
62/// let path = Utf8UnixPath::new("/foo/bar.txt");
63///
64/// // Same as Utf8Path<Utf8WindowsEncoding>
65/// let path = Utf8WindowsPath::new(r"C:\foo\bar.txt");
66/// ```
67///
68/// To mirror the design of Rust's standard library, you can access
69/// the path associated with the compiled rust platform using [`Utf8NativePath`],
70/// which itself is an alias to one of the other choices:
71///
72/// ```
73/// use typed_path::Utf8NativePath;
74///
75/// // On Unix, this would be Utf8UnixPath aka Utf8Path<Utf8UnixEncoding>
76/// // On Windows, this would be Utf8WindowsPath aka Utf8Path<Utf8WindowsEncoding>
77/// let path = Utf8NativePath::new("/foo/bar.txt");
78/// ```
79///
80/// [`Utf8NativePath`]: crate::Utf8NativePath
81#[repr(transparent)]
82pub struct Utf8Path<T>
83where
84    T: Utf8Encoding,
85{
86    /// Encoding associated with path buf
87    _encoding: PhantomData<T>,
88
89    /// Path as an unparsed str slice
90    pub(crate) inner: str,
91}
92
93impl<T> Utf8Path<T>
94where
95    T: Utf8Encoding,
96{
97    /// Directly wraps a str slice as a `Utf8Path` slice.
98    ///
99    /// This is a cost-free conversion.
100    ///
101    /// # Examples
102    ///
103    /// ```
104    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
105    ///
106    /// // NOTE: A path cannot be created on its own without a defined encoding
107    /// Utf8Path::<Utf8UnixEncoding>::new("foo.txt");
108    /// ```
109    ///
110    /// You can create `Utf8Path`s from `String`s, or even other `Utf8Path`s:
111    ///
112    /// ```
113    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
114    ///
115    /// // NOTE: A path cannot be created on its own without a defined encoding
116    /// let string = String::from("foo.txt");
117    /// let from_string = Utf8Path::<Utf8UnixEncoding>::new(&string);
118    /// let from_path = Utf8Path::new(&from_string);
119    /// assert_eq!(from_string, from_path);
120    /// ```
121    ///
122    /// There are also handy aliases to the `Utf8Path` with [`Utf8Encoding`]:
123    ///
124    /// ```
125    /// use typed_path::Utf8UnixPath;
126    ///
127    /// let string = String::from("foo.txt");
128    /// let from_string = Utf8UnixPath::new(&string);
129    /// let from_path = Utf8UnixPath::new(&from_string);
130    /// assert_eq!(from_string, from_path);
131    /// ```
132    #[inline]
133    pub fn new<S: AsRef<str> + ?Sized>(s: &S) -> &Self {
134        unsafe { &*(s.as_ref() as *const str as *const Self) }
135    }
136
137    /// Yields the underlying [`str`] slice.
138    ///
139    /// # Examples
140    ///
141    /// ```
142    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
143    ///
144    /// // NOTE: A path cannot be created on its own without a defined encoding
145    /// let s = Utf8Path::<Utf8UnixEncoding>::new("foo.txt").as_str();
146    /// assert_eq!(s, "foo.txt");
147    /// ```
148    pub fn as_str(&self) -> &str {
149        &self.inner
150    }
151
152    /// Converts a `Utf8Path` to an owned [`Utf8PathBuf`].
153    ///
154    /// # Examples
155    ///
156    /// ```
157    /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
158    ///
159    /// // NOTE: A path cannot be created on its own without a defined encoding
160    /// let path_buf = Utf8Path::<Utf8UnixEncoding>::new("foo.txt").to_path_buf();
161    /// assert_eq!(path_buf, Utf8PathBuf::from("foo.txt"));
162    /// ```
163    pub fn to_path_buf(&self) -> Utf8PathBuf<T> {
164        Utf8PathBuf {
165            _encoding: PhantomData,
166            inner: self.inner.to_owned(),
167        }
168    }
169
170    /// Returns `true` if the `Utf8Path` is absolute, i.e., if it is independent of
171    /// the current directory.
172    ///
173    /// * On Unix ([`Utf8UnixPath`]]), a path is absolute if it starts with the root, so
174    ///   `is_absolute` and [`has_root`] are equivalent.
175    ///
176    /// * On Windows ([`Utf8WindowsPath`]), a path is absolute if it has a prefix and starts with
177    ///   the root: `c:\windows` is absolute, while `c:temp` and `\temp` are not.
178    ///
179    /// [`Utf8UnixPath`]: crate::Utf8UnixPath
180    /// [`Utf8WindowsPath`]: crate::Utf8WindowsPath
181    ///
182    /// # Examples
183    ///
184    /// ```
185    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
186    ///
187    /// // NOTE: A path cannot be created on its own without a defined encoding
188    /// assert!(!Utf8Path::<Utf8UnixEncoding>::new("foo.txt").is_absolute());
189    /// ```
190    ///
191    /// [`has_root`]: Utf8Path::has_root
192    pub fn is_absolute(&self) -> bool {
193        self.components().is_absolute()
194    }
195
196    /// Returns `true` if the `Utf8Path` is relative, i.e., not absolute.
197    ///
198    /// See [`is_absolute`]'s documentation for more details.
199    ///
200    /// # Examples
201    ///
202    /// ```
203    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
204    ///
205    /// // NOTE: A path cannot be created on its own without a defined encoding
206    /// assert!(Utf8Path::<Utf8UnixEncoding>::new("foo.txt").is_relative());
207    /// ```
208    ///
209    /// [`is_absolute`]: Utf8Path::is_absolute
210    #[inline]
211    pub fn is_relative(&self) -> bool {
212        !self.is_absolute()
213    }
214
215    /// Returns `true` if the path is valid, meaning that all of its components are valid.
216    ///
217    /// See [`Utf8Component::is_valid`]'s documentation for more details.
218    ///
219    /// # Examples
220    ///
221    /// ```
222    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
223    ///
224    /// // NOTE: A path cannot be created on its own without a defined encoding
225    /// assert!(Utf8Path::<Utf8UnixEncoding>::new("foo.txt").is_valid());
226    /// assert!(!Utf8Path::<Utf8UnixEncoding>::new("foo\0.txt").is_valid());
227    /// ```
228    ///
229    /// [`Utf8Component::is_valid`]: crate::Utf8Component::is_valid
230    pub fn is_valid(&self) -> bool {
231        self.components().all(|c| c.is_valid())
232    }
233
234    /// Returns `true` if the `Utf8Path` has a root.
235    ///
236    /// * On Unix ([`Utf8UnixPath`]), a path has a root if it begins with `/`.
237    ///
238    /// * On Windows ([`Utf8WindowsPath`]), a path has a root if it:
239    ///     * has no prefix and begins with a separator, e.g., `\windows`
240    ///     * has a prefix followed by a separator, e.g., `c:\windows` but not `c:windows`
241    ///     * has any non-disk prefix, e.g., `\\server\share`
242    ///
243    /// [`Utf8UnixPath`]: crate::Utf8UnixPath
244    /// [`Utf8WindowsPath`]: crate::Utf8WindowsPath
245    ///
246    /// # Examples
247    ///
248    /// ```
249    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
250    ///
251    /// // NOTE: A path cannot be created on its own without a defined encoding
252    /// assert!(Utf8Path::<Utf8UnixEncoding>::new("/etc/passwd").has_root());
253    /// ```
254    #[inline]
255    pub fn has_root(&self) -> bool {
256        self.components().has_root()
257    }
258
259    /// Returns the `Utf8Path` without its final component, if there is one.
260    ///
261    /// Returns [`None`] if the path terminates in a root or prefix.
262    ///
263    /// # Examples
264    ///
265    /// ```
266    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
267    ///
268    /// // NOTE: A path cannot be created on its own without a defined encoding
269    /// let path = Utf8Path::<Utf8UnixEncoding>::new("/foo/bar");
270    /// let parent = path.parent().unwrap();
271    /// assert_eq!(parent, Utf8Path::new("/foo"));
272    ///
273    /// let grand_parent = parent.parent().unwrap();
274    /// assert_eq!(grand_parent, Utf8Path::new("/"));
275    /// assert_eq!(grand_parent.parent(), None);
276    /// ```
277    pub fn parent(&self) -> Option<&Self> {
278        let mut comps = self.components();
279        let comp = comps.next_back();
280        comp.and_then(|p| {
281            if !p.is_root() {
282                Some(Self::new(comps.as_str()))
283            } else {
284                None
285            }
286        })
287    }
288
289    /// Produces an iterator over `Utf8Path` and its ancestors.
290    ///
291    /// The iterator will yield the `Utf8Path` that is returned if the [`parent`] method is used zero
292    /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`,
293    /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns
294    /// [`None`], the iterator will do likewise. The iterator will always yield at least one value,
295    /// namely `&self`.
296    ///
297    /// # Examples
298    ///
299    /// ```
300    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
301    ///
302    /// // NOTE: A path cannot be created on its own without a defined encoding
303    /// let mut ancestors = Utf8Path::<Utf8UnixEncoding>::new("/foo/bar").ancestors();
304    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("/foo/bar")));
305    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("/foo")));
306    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("/")));
307    /// assert_eq!(ancestors.next(), None);
308    ///
309    /// // NOTE: A path cannot be created on its own without a defined encoding
310    /// let mut ancestors = Utf8Path::<Utf8UnixEncoding>::new("../foo/bar").ancestors();
311    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("../foo/bar")));
312    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("../foo")));
313    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("..")));
314    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("")));
315    /// assert_eq!(ancestors.next(), None);
316    /// ```
317    ///
318    /// [`parent`]: Utf8Path::parent
319    #[inline]
320    pub fn ancestors(&self) -> Utf8Ancestors<'_, T> {
321        Utf8Ancestors { next: Some(self) }
322    }
323
324    /// Returns the final component of the `Utf8Path`, if there is one.
325    ///
326    /// If the path is a normal file, this is the file name. If it's the path of a directory, this
327    /// is the directory name.
328    ///
329    /// Returns [`None`] if the path terminates in `..`.
330    ///
331    /// # Examples
332    ///
333    /// ```
334    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
335    ///
336    /// // NOTE: A path cannot be created on its own without a defined encoding
337    /// assert_eq!(Some("bin"), Utf8Path::<Utf8UnixEncoding>::new("/usr/bin/").file_name());
338    /// assert_eq!(Some("foo.txt"), Utf8Path::<Utf8UnixEncoding>::new("tmp/foo.txt").file_name());
339    /// assert_eq!(Some("foo.txt"), Utf8Path::<Utf8UnixEncoding>::new("foo.txt/.").file_name());
340    /// assert_eq!(Some("foo.txt"), Utf8Path::<Utf8UnixEncoding>::new("foo.txt/.//").file_name());
341    /// assert_eq!(None, Utf8Path::<Utf8UnixEncoding>::new("foo.txt/..").file_name());
342    /// assert_eq!(None, Utf8Path::<Utf8UnixEncoding>::new("/").file_name());
343    /// ```
344    pub fn file_name(&self) -> Option<&str> {
345        match self.components().next_back() {
346            Some(p) => {
347                if p.is_normal() {
348                    Some(p.as_str())
349                } else {
350                    None
351                }
352            }
353            None => None,
354        }
355    }
356
357    /// Returns a path that, when joined onto `base`, yields `self`.
358    ///
359    /// # Errors
360    ///
361    /// If `base` is not a prefix of `self` (i.e., [`starts_with`]
362    /// returns `false`), returns [`Err`].
363    ///
364    /// [`starts_with`]: Utf8Path::starts_with
365    ///
366    /// # Examples
367    ///
368    /// ```
369    /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
370    ///
371    /// // NOTE: A path cannot be created on its own without a defined encoding
372    /// let path = Utf8Path::<Utf8UnixEncoding>::new("/test/haha/foo.txt");
373    ///
374    /// assert_eq!(path.strip_prefix("/"), Ok(Utf8Path::new("test/haha/foo.txt")));
375    /// assert_eq!(path.strip_prefix("/test"), Ok(Utf8Path::new("haha/foo.txt")));
376    /// assert_eq!(path.strip_prefix("/test/"), Ok(Utf8Path::new("haha/foo.txt")));
377    /// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Utf8Path::new("")));
378    /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Utf8Path::new("")));
379    ///
380    /// assert!(path.strip_prefix("test").is_err());
381    /// assert!(path.strip_prefix("/haha").is_err());
382    ///
383    /// let prefix = Utf8PathBuf::<Utf8UnixEncoding>::from("/test/");
384    /// assert_eq!(path.strip_prefix(prefix), Ok(Utf8Path::new("haha/foo.txt")));
385    /// ```
386    pub fn strip_prefix<P>(&self, base: P) -> Result<&Utf8Path<T>, StripPrefixError>
387    where
388        P: AsRef<Utf8Path<T>>,
389    {
390        self._strip_prefix(base.as_ref())
391    }
392
393    fn _strip_prefix(&self, base: &Utf8Path<T>) -> Result<&Utf8Path<T>, StripPrefixError> {
394        match helpers::iter_after(self.components(), base.components()) {
395            Some(c) => Ok(Utf8Path::new(c.as_str())),
396            None => Err(StripPrefixError(())),
397        }
398    }
399
400    /// Determines whether `base` is a prefix of `self`.
401    ///
402    /// Only considers whole path components to match.
403    ///
404    /// # Examples
405    ///
406    /// ```
407    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
408    ///
409    /// // NOTE: A path cannot be created on its own without a defined encoding
410    /// let path = Utf8Path::<Utf8UnixEncoding>::new("/etc/passwd");
411    ///
412    /// assert!(path.starts_with("/etc"));
413    /// assert!(path.starts_with("/etc/"));
414    /// assert!(path.starts_with("/etc/passwd"));
415    /// assert!(path.starts_with("/etc/passwd/")); // extra slash is okay
416    /// assert!(path.starts_with("/etc/passwd///")); // multiple extra slashes are okay
417    ///
418    /// assert!(!path.starts_with("/e"));
419    /// assert!(!path.starts_with("/etc/passwd.txt"));
420    ///
421    /// assert!(!Utf8Path::<Utf8UnixEncoding>::new("/etc/foo.rs").starts_with("/etc/foo"));
422    /// ```
423    pub fn starts_with<P>(&self, base: P) -> bool
424    where
425        P: AsRef<Utf8Path<T>>,
426    {
427        self._starts_with(base.as_ref())
428    }
429
430    fn _starts_with(&self, base: &Utf8Path<T>) -> bool {
431        helpers::iter_after(self.components(), base.components()).is_some()
432    }
433
434    /// Determines whether `child` is a suffix of `self`.
435    ///
436    /// Only considers whole path components to match.
437    ///
438    /// # Examples
439    ///
440    /// ```
441    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
442    ///
443    /// // NOTE: A path cannot be created on its own without a defined encoding
444    /// let path = Utf8Path::<Utf8UnixEncoding>::new("/etc/resolv.conf");
445    ///
446    /// assert!(path.ends_with("resolv.conf"));
447    /// assert!(path.ends_with("etc/resolv.conf"));
448    /// assert!(path.ends_with("/etc/resolv.conf"));
449    ///
450    /// assert!(!path.ends_with("/resolv.conf"));
451    /// assert!(!path.ends_with("conf")); // use .extension() instead
452    /// ```
453    pub fn ends_with<P>(&self, child: P) -> bool
454    where
455        P: AsRef<Utf8Path<T>>,
456    {
457        self._ends_with(child.as_ref())
458    }
459
460    fn _ends_with(&self, child: &Utf8Path<T>) -> bool {
461        helpers::iter_after(self.components().rev(), child.components().rev()).is_some()
462    }
463
464    /// Extracts the stem (non-extension) portion of [`self.file_name`].
465    ///
466    /// [`self.file_name`]: Utf8Path::file_name
467    ///
468    /// The stem is:
469    ///
470    /// * [`None`], if there is no file name;
471    /// * The entire file name if there is no embedded `.`;
472    /// * The entire file name if the file name begins with `.` and has no other `.`s within;
473    /// * Otherwise, the portion of the file name before the final `.`
474    ///
475    /// # Examples
476    ///
477    /// ```
478    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
479    ///
480    /// // NOTE: A path cannot be created on its own without a defined encoding
481    /// assert_eq!("foo", Utf8Path::<Utf8UnixEncoding>::new("foo.rs").file_stem().unwrap());
482    /// assert_eq!("foo.tar", Utf8Path::<Utf8UnixEncoding>::new("foo.tar.gz").file_stem().unwrap());
483    /// ```
484    ///
485    pub fn file_stem(&self) -> Option<&str> {
486        self.file_name()
487            .map(helpers::rsplit_file_at_dot)
488            .and_then(|(before, after)| before.or(after))
489    }
490
491    /// Extracts the extension of [`self.file_name`], if possible.
492    ///
493    /// The extension is:
494    ///
495    /// * [`None`], if there is no file name;
496    /// * [`None`], if there is no embedded `.`;
497    /// * [`None`], if the file name begins with `.` and has no other `.`s within;
498    /// * Otherwise, the portion of the file name after the final `.`
499    ///
500    /// [`self.file_name`]: Utf8Path::file_name
501    ///
502    /// # Examples
503    ///
504    /// ```
505    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
506    ///
507    /// // NOTE: A path cannot be created on its own without a defined encoding
508    /// assert_eq!("rs", Utf8Path::<Utf8UnixEncoding>::new("foo.rs").extension().unwrap());
509    /// assert_eq!("gz", Utf8Path::<Utf8UnixEncoding>::new("foo.tar.gz").extension().unwrap());
510    /// ```
511    pub fn extension(&self) -> Option<&str> {
512        self.file_name()
513            .map(helpers::rsplit_file_at_dot)
514            .and_then(|(before, after)| before.and(after))
515    }
516
517    /// Returns an owned [`Utf8PathBuf`] by resolving `..` and `.` segments.
518    ///
519    /// When multiple, sequential path segment separation characters are found (e.g. `/` for Unix
520    /// and either `\` or `/` on Windows), they are replaced by a single instance of the
521    /// platform-specific path segment separator (`/` on Unix and `\` on Windows).
522    ///
523    /// # Examples
524    ///
525    /// ```
526    /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
527    ///
528    /// // NOTE: A path cannot be created on its own without a defined encoding
529    /// assert_eq!(
530    ///     Utf8Path::<Utf8UnixEncoding>::new("foo/bar//baz/./asdf/quux/..").normalize(),
531    ///     Utf8PathBuf::from("foo/bar/baz/asdf"),
532    /// );
533    /// ```
534    ///
535    /// When starting with a root directory, any `..` segment whose parent is the root directory
536    /// will be filtered out:
537    ///
538    /// ```
539    /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
540    ///
541    /// // NOTE: A path cannot be created on its own without a defined encoding
542    /// assert_eq!(
543    ///     Utf8Path::<Utf8UnixEncoding>::new("/../foo").normalize(),
544    ///     Utf8PathBuf::from("/foo"),
545    /// );
546    /// ```
547    ///
548    /// If any `..` is left unresolved as the path is relative and no parent is found, it is
549    /// discarded:
550    ///
551    /// ```
552    /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding, Utf8WindowsEncoding};
553    ///
554    /// assert_eq!(
555    ///     Utf8Path::<Utf8UnixEncoding>::new("../foo/..").normalize(),
556    ///     Utf8PathBuf::from(""),
557    /// );
558    ///
559    /// //Windows prefixes also count this way, but the prefix remains
560    /// assert_eq!(
561    ///     Utf8Path::<Utf8WindowsEncoding>::new(r"C:..\foo\..").normalize(),
562    ///     Utf8PathBuf::from(r"C:"),
563    /// );
564    /// ```
565    pub fn normalize(&self) -> Utf8PathBuf<T> {
566        let mut components = Vec::new();
567        for component in self.components() {
568            if !component.is_current() && !component.is_parent() {
569                components.push(component);
570            } else if component.is_parent() {
571                if let Some(last) = components.last() {
572                    if last.is_normal() {
573                        components.pop();
574                    }
575                }
576            }
577        }
578
579        let mut path = Utf8PathBuf::<T>::new();
580
581        for component in components {
582            path.push(component.as_str());
583        }
584
585        path
586    }
587
588    /// Converts a path to an absolute form by [`normalizing`] the path, returning a
589    /// [`Utf8PathBuf`].
590    ///
591    /// In the case that the path is relative, the current working directory is prepended prior to
592    /// normalizing.
593    ///
594    /// [`normalizing`]: Utf8Path::normalize
595    ///
596    /// # Examples
597    ///
598    /// ```
599    /// use typed_path::{utils, Utf8Path, Utf8UnixEncoding};
600    ///
601    /// // With an absolute path, it is just normalized
602    /// let path = Utf8Path::<Utf8UnixEncoding>::new("/a/b/../c/./d");
603    /// assert_eq!(path.absolutize().unwrap(), Utf8Path::new("/a/c/d"));
604    ///
605    /// // With a relative path, it is first joined with the current working directory
606    /// // and then normalized
607    /// let cwd = utils::utf8_current_dir().unwrap().with_encoding::<Utf8UnixEncoding>();
608    /// let path = cwd.join(Utf8Path::new("a/b/../c/./d"));
609    /// assert_eq!(path.absolutize().unwrap(), cwd.join(Utf8Path::new("a/c/d")));
610    /// ```
611    #[cfg(all(feature = "std", not(target_family = "wasm")))]
612    pub fn absolutize(&self) -> std::io::Result<Utf8PathBuf<T>> {
613        if self.is_absolute() {
614            Ok(self.normalize())
615        } else {
616            // Get the cwd as a platform path and convert to this path's encoding
617            let cwd = crate::utils::utf8_current_dir()?.with_encoding();
618
619            Ok(cwd.join(self).normalize())
620        }
621    }
622
623    /// Creates an owned [`Utf8PathBuf`] with `path` adjoined to `self`.
624    ///
625    /// See [`Utf8PathBuf::push`] for more details on what it means to adjoin a path.
626    ///
627    /// # Examples
628    ///
629    /// ```
630    /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
631    ///
632    /// // NOTE: A path cannot be created on its own without a defined encoding
633    /// assert_eq!(
634    ///     Utf8Path::<Utf8UnixEncoding>::new("/etc").join("passwd"),
635    ///     Utf8PathBuf::from("/etc/passwd"),
636    /// );
637    /// ```
638    pub fn join<P: AsRef<Utf8Path<T>>>(&self, path: P) -> Utf8PathBuf<T> {
639        self._join(path.as_ref())
640    }
641
642    fn _join(&self, path: &Utf8Path<T>) -> Utf8PathBuf<T> {
643        let mut buf = self.to_path_buf();
644        buf.push(path);
645        buf
646    }
647
648    /// Creates an owned [`Utf8PathBuf`] with `path` adjoined to `self`, checking the `path` to
649    /// ensure it is safe to join. _When dealing with user-provided paths, this is the preferred
650    /// method._
651    ///
652    /// See [`Utf8PathBuf::push_checked`] for more details on what it means to adjoin a path
653    /// safely.
654    ///
655    /// # Examples
656    ///
657    /// ```
658    /// use typed_path::{CheckedPathError, Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
659    ///
660    /// // NOTE: A path cannot be created on its own without a defined encoding
661    /// let path = Utf8Path::<Utf8UnixEncoding>::new("/etc");
662    ///
663    /// // A valid path can be joined onto the existing one
664    /// assert_eq!(path.join_checked("passwd"), Ok(Utf8PathBuf::from("/etc/passwd")));
665    ///
666    /// // An invalid path will result in an error
667    /// assert_eq!(path.join_checked("/sneaky/replacement"), Err(CheckedPathError::UnexpectedRoot));
668    /// ```
669    pub fn join_checked<P: AsRef<Utf8Path<T>>>(
670        &self,
671        path: P,
672    ) -> Result<Utf8PathBuf<T>, CheckedPathError> {
673        self._join_checked(path.as_ref())
674    }
675
676    fn _join_checked(&self, path: &Utf8Path<T>) -> Result<Utf8PathBuf<T>, CheckedPathError> {
677        let mut buf = self.to_path_buf();
678        buf.push_checked(path)?;
679        Ok(buf)
680    }
681
682    /// Creates an owned [`Utf8PathBuf`] like `self` but with the given file name.
683    ///
684    /// See [`Utf8PathBuf::set_file_name`] for more details.
685    ///
686    /// # Examples
687    ///
688    /// ```
689    /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
690    ///
691    /// // NOTE: A path cannot be created on its own without a defined encoding
692    /// let path = Utf8Path::<Utf8UnixEncoding>::new("/tmp/foo.txt");
693    /// assert_eq!(path.with_file_name("bar.txt"), Utf8PathBuf::from("/tmp/bar.txt"));
694    ///
695    /// // NOTE: A path cannot be created on its own without a defined encoding
696    /// let path = Utf8Path::<Utf8UnixEncoding>::new("/tmp");
697    /// assert_eq!(path.with_file_name("var"), Utf8PathBuf::from("/var"));
698    /// ```
699    pub fn with_file_name<S: AsRef<str>>(&self, file_name: S) -> Utf8PathBuf<T> {
700        self._with_file_name(file_name.as_ref())
701    }
702
703    fn _with_file_name(&self, file_name: &str) -> Utf8PathBuf<T> {
704        let mut buf = self.to_path_buf();
705        buf.set_file_name(file_name);
706        buf
707    }
708
709    /// Creates an owned [`Utf8PathBuf`] like `self` but with the given extension.
710    ///
711    /// See [`Utf8PathBuf::set_extension`] for more details.
712    ///
713    /// # Examples
714    ///
715    /// ```
716    /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
717    ///
718    /// // NOTE: A path cannot be created on its own without a defined encoding
719    /// let path = Utf8Path::<Utf8UnixEncoding>::new("foo.rs");
720    /// assert_eq!(path.with_extension("txt"), Utf8PathBuf::from("foo.txt"));
721    ///
722    /// // NOTE: A path cannot be created on its own without a defined encoding
723    /// let path = Utf8Path::<Utf8UnixEncoding>::new("foo.tar.gz");
724    /// assert_eq!(path.with_extension(""), Utf8PathBuf::from("foo.tar"));
725    /// assert_eq!(path.with_extension("xz"), Utf8PathBuf::from("foo.tar.xz"));
726    /// assert_eq!(path.with_extension("").with_extension("txt"), Utf8PathBuf::from("foo.txt"));
727    /// ```
728    pub fn with_extension<S: AsRef<str>>(&self, extension: S) -> Utf8PathBuf<T> {
729        self._with_extension(extension.as_ref())
730    }
731
732    fn _with_extension(&self, extension: &str) -> Utf8PathBuf<T> {
733        let mut buf = self.to_path_buf();
734        buf.set_extension(extension);
735        buf
736    }
737
738    /// Produces an iterator over the [`Utf8Component`]s of the path.
739    ///
740    /// When parsing the path, there is a small amount of normalization:
741    ///
742    /// * Repeated separators are ignored, so `a/b` and `a//b` both have
743    ///   `a` and `b` as components.
744    ///
745    /// * Occurrences of `.` are normalized away, except if they are at the
746    ///   beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and
747    ///   `a/b` all have `a` and `b` as components, but `./a/b` starts with
748    ///   an additional [`CurDir`] component.
749    ///
750    /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent.
751    ///
752    /// Note that no other normalization takes place; in particular, `a/c`
753    /// and `a/b/../c` are distinct, to account for the possibility that `b`
754    /// is a symbolic link (so its parent isn't `a`).
755    ///
756    /// # Examples
757    ///
758    /// ```
759    /// use typed_path::{Utf8Path, Utf8UnixComponent, Utf8UnixEncoding};
760    ///
761    /// // NOTE: A path cannot be created on its own without a defined encoding
762    /// let mut components = Utf8Path::<Utf8UnixEncoding>::new("/tmp/foo.txt").components();
763    ///
764    /// assert_eq!(components.next(), Some(Utf8UnixComponent::RootDir));
765    /// assert_eq!(components.next(), Some(Utf8UnixComponent::Normal("tmp")));
766    /// assert_eq!(components.next(), Some(Utf8UnixComponent::Normal("foo.txt")));
767    /// assert_eq!(components.next(), None)
768    /// ```
769    ///
770    /// [`CurDir`]: crate::unix::UnixComponent::CurDir
771    pub fn components(&self) -> <T as Utf8Encoding>::Components<'_> {
772        T::components(&self.inner)
773    }
774
775    /// Produces an iterator over the path's components viewed as [`str`] slices.
776    ///
777    /// For more information about the particulars of how the path is separated
778    /// into components, see [`components`].
779    ///
780    /// [`components`]: Utf8Path::components
781    ///
782    /// # Examples
783    ///
784    /// ```
785    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
786    ///
787    /// // NOTE: A path cannot be created on its own without a defined encoding
788    /// let mut it = Utf8Path::<Utf8UnixEncoding>::new("/tmp/foo.txt").iter();
789    ///
790    /// assert_eq!(it.next(), Some(typed_path::constants::unix::SEPARATOR_STR));
791    /// assert_eq!(it.next(), Some("tmp"));
792    /// assert_eq!(it.next(), Some("foo.txt"));
793    /// assert_eq!(it.next(), None)
794    /// ```
795    #[inline]
796    pub fn iter(&self) -> Utf8Iter<'_, T> {
797        Utf8Iter::new(self.components())
798    }
799
800    /// Creates an owned [`Utf8PathBuf`] like `self` but with a different encoding.
801    ///
802    /// # Note
803    ///
804    /// As part of the process of converting between encodings, the path will need to be rebuilt.
805    /// This involves [`pushing`] each component, which may result in differences in the resulting
806    /// path such as resolving `.` and `..` early or other unexpected side effects.
807    ///
808    /// [`pushing`]: Utf8PathBuf::push
809    ///
810    /// # Examples
811    ///
812    /// ```
813    /// use typed_path::{Utf8Path, Utf8UnixEncoding, Utf8WindowsEncoding};
814    ///
815    /// // Convert from Unix to Windows
816    /// let unix_path = Utf8Path::<Utf8UnixEncoding>::new("/tmp/foo.txt");
817    /// let windows_path = unix_path.with_encoding::<Utf8WindowsEncoding>();
818    /// assert_eq!(windows_path, Utf8Path::<Utf8WindowsEncoding>::new(r"\tmp\foo.txt"));
819    ///
820    /// // Converting from Windows to Unix will drop any prefix
821    /// let windows_path = Utf8Path::<Utf8WindowsEncoding>::new(r"C:\tmp\foo.txt");
822    /// let unix_path = windows_path.with_encoding::<Utf8UnixEncoding>();
823    /// assert_eq!(unix_path, Utf8Path::<Utf8UnixEncoding>::new(r"/tmp/foo.txt"));
824    ///
825    /// // Converting to itself should retain everything
826    /// let path = Utf8Path::<Utf8WindowsEncoding>::new(r"C:\tmp\foo.txt");
827    /// assert_eq!(
828    ///     path.with_encoding::<Utf8WindowsEncoding>(),
829    ///     Utf8Path::<Utf8WindowsEncoding>::new(r"C:\tmp\foo.txt"),
830    /// );
831    /// ```
832    pub fn with_encoding<U>(&self) -> Utf8PathBuf<U>
833    where
834        U: Utf8Encoding,
835    {
836        // If we're the same, just return the path buf, which
837        // we do with a fancy trick to convert it
838        if T::label() == U::label() {
839            Utf8Path::new(self.as_str()).to_path_buf()
840        } else {
841            // Otherwise, we have to rebuild the path from the components
842            let mut path = Utf8PathBuf::new();
843
844            // For root, current, and parent we specially handle to convert
845            // to the appropriate type, otherwise we pass along as-is
846            for component in self.components() {
847                if component.is_root() {
848                    path.push(<
849                        <<U as Utf8Encoding>::Components<'_> as Utf8Components>::Component
850                        as Utf8Component
851                    >::root().as_str());
852                } else if component.is_current() {
853                    path.push(<
854                        <<U as Utf8Encoding>::Components<'_> as Utf8Components>::Component
855                        as Utf8Component
856                    >::current().as_str());
857                } else if component.is_parent() {
858                    path.push(<
859                        <<U as Utf8Encoding>::Components<'_> as Utf8Components>::Component
860                        as Utf8Component
861                    >::parent().as_str());
862                } else {
863                    path.push(component.as_str());
864                }
865            }
866
867            path
868        }
869    }
870
871    /// Like [`with_encoding`], creates an owned [`Utf8PathBuf`] like `self` but with a different
872    /// encoding. Additionally, checks to ensure that the produced path will be valid.
873    ///
874    /// # Note
875    ///
876    /// As part of the process of converting between encodings, the path will need to be rebuilt.
877    /// This involves [`pushing and checking`] each component, which may result in differences in
878    /// the resulting path such as resolving `.` and `..` early or other unexpected side effects.
879    ///
880    /// [`pushing and checking`]: Utf8PathBuf::push_checked
881    /// [`with_encoding`]: Utf8Path::with_encoding
882    ///
883    /// # Examples
884    ///
885    /// ```
886    /// use typed_path::{CheckedPathError, Utf8Path, Utf8UnixEncoding, Utf8WindowsEncoding};
887    ///
888    /// // Convert from Unix to Windows
889    /// let unix_path = Utf8Path::<Utf8UnixEncoding>::new("/tmp/foo.txt");
890    /// let windows_path = unix_path.with_encoding_checked::<Utf8WindowsEncoding>().unwrap();
891    /// assert_eq!(windows_path, Utf8Path::<Utf8WindowsEncoding>::new(r"\tmp\foo.txt"));
892    ///
893    /// // Converting from Windows to Unix will drop any prefix
894    /// let windows_path = Utf8Path::<Utf8WindowsEncoding>::new(r"C:\tmp\foo.txt");
895    /// let unix_path = windows_path.with_encoding_checked::<Utf8UnixEncoding>().unwrap();
896    /// assert_eq!(unix_path, Utf8Path::<Utf8UnixEncoding>::new(r"/tmp/foo.txt"));
897    ///
898    /// // Converting from Unix to Windows with invalid filename characters like `|` should fail
899    /// let unix_path = Utf8Path::<Utf8UnixEncoding>::new("/|invalid|/foo.txt");
900    /// assert_eq!(
901    ///     unix_path.with_encoding_checked::<Utf8WindowsEncoding>(),
902    ///     Err(CheckedPathError::InvalidFilename),
903    /// );
904    ///
905    /// // Converting from Unix to Windows with unexpected prefix embedded in path should fail
906    /// let unix_path = Utf8Path::<Utf8UnixEncoding>::new("/path/c:/foo.txt");
907    /// assert_eq!(
908    ///     unix_path.with_encoding_checked::<Utf8WindowsEncoding>(),
909    ///     Err(CheckedPathError::UnexpectedPrefix),
910    /// );
911    /// ```
912    pub fn with_encoding_checked<U>(&self) -> Result<Utf8PathBuf<U>, CheckedPathError>
913    where
914        U: Utf8Encoding,
915    {
916        let mut path = Utf8PathBuf::new();
917
918        // For root, current, and parent we specially handle to convert to the appropriate type,
919        // otherwise we attempt to push using the checked variant, which will ensure that the
920        // destination encoding is respected
921        for component in self.components() {
922            if component.is_root() {
923                path.push(<
924                        <<U as Utf8Encoding>::Components<'_> as Utf8Components>::Component
925                        as Utf8Component
926                    >::root().as_str());
927            } else if component.is_current() {
928                path.push(<
929                        <<U as Utf8Encoding>::Components<'_> as Utf8Components>::Component
930                        as Utf8Component
931                    >::current().as_str());
932            } else if component.is_parent() {
933                path.push(<
934                        <<U as Utf8Encoding>::Components<'_> as Utf8Components>::Component
935                        as Utf8Component
936                    >::parent().as_str());
937            } else {
938                path.push_checked(component.as_str())?;
939            }
940        }
941
942        Ok(path)
943    }
944
945    /// Converts a [`Box<Utf8Path>`](Box) into a
946    /// [`Utf8PathBuf`] without copying or allocating.
947    pub fn into_path_buf(self: Box<Utf8Path<T>>) -> Utf8PathBuf<T> {
948        let rw = Box::into_raw(self) as *mut str;
949        let inner = unsafe { Box::from_raw(rw) };
950        Utf8PathBuf {
951            _encoding: PhantomData,
952            inner: inner.into_string(),
953        }
954    }
955
956    /// Converts a non-UTF-8 [`Path`] to a UTF-8 [`Utf8PathBuf`] by checking that the path contains
957    /// valid UTF-8.
958    ///
959    /// # Errors
960    ///
961    /// Returns `Err` if the path is not UTF-8 with a description as to why the
962    /// provided component is not UTF-8.
963    ///
964    /// # Examples
965    ///
966    /// ```
967    /// use typed_path::{Path, Utf8Path, UnixEncoding, Utf8UnixEncoding};
968    ///
969    /// let path = Path::<UnixEncoding>::new(&[0xf0, 0x9f, 0x92, 0x96]);
970    /// let utf8_path = Utf8Path::<Utf8UnixEncoding>::from_bytes_path(&path).unwrap();
971    /// assert_eq!(utf8_path.as_str(), "💖");
972    /// ```
973    pub fn from_bytes_path<U>(path: &Path<U>) -> Result<&Self, Utf8Error>
974    where
975        U: Encoding,
976    {
977        Ok(Self::new(core::str::from_utf8(path.as_bytes())?))
978    }
979
980    /// Converts a non-UTF-8 [`Path`] to a UTF-8 [`Utf8Path`] without checking that the path
981    /// contains valid UTF-8.
982    ///
983    /// See the safe version, [`from_bytes_path`], for more information.
984    ///
985    /// [`from_bytes_path`]: Utf8Path::from_bytes_path
986    ///
987    /// # Safety
988    ///
989    /// The path passed in must be valid UTF-8.
990    ///
991    /// # Examples
992    ///
993    /// ```
994    /// use typed_path::{Path, Utf8Path, UnixEncoding, Utf8UnixEncoding};
995    ///
996    /// let path = Path::<UnixEncoding>::new(&[0xf0, 0x9f, 0x92, 0x96]);
997    /// let utf8_path = unsafe {
998    ///     Utf8Path::<Utf8UnixEncoding>::from_bytes_path_unchecked(&path)
999    /// };
1000    /// assert_eq!(utf8_path.as_str(), "💖");
1001    /// ```
1002    pub unsafe fn from_bytes_path_unchecked<U>(path: &Path<U>) -> &Self
1003    where
1004        U: Encoding,
1005    {
1006        Self::new(core::str::from_utf8_unchecked(path.as_bytes()))
1007    }
1008
1009    /// Converts a UTF-8 [`Utf8Path`] to a non-UTF-8 [`Path`].
1010    ///
1011    /// # Examples
1012    ///
1013    /// ```
1014    /// use typed_path::{Path, Utf8Path, UnixEncoding, Utf8UnixEncoding};
1015    ///
1016    /// let utf8_path = Utf8Path::<Utf8UnixEncoding>::new("💖");
1017    /// let path = utf8_path.as_bytes_path::<UnixEncoding>();
1018    /// assert_eq!(path.as_bytes(), &[0xf0, 0x9f, 0x92, 0x96]);
1019    /// ```
1020    pub fn as_bytes_path<U>(&self) -> &Path<U>
1021    where
1022        U: Encoding,
1023    {
1024        Path::new(self.as_str())
1025    }
1026}
1027
1028impl<T> Clone for Box<Utf8Path<T>>
1029where
1030    T: Utf8Encoding,
1031{
1032    fn clone(&self) -> Self {
1033        self.to_path_buf().into_boxed_path()
1034    }
1035}
1036
1037impl<T> AsRef<[u8]> for Utf8Path<T>
1038where
1039    T: Utf8Encoding,
1040{
1041    #[inline]
1042    fn as_ref(&self) -> &[u8] {
1043        self.inner.as_bytes()
1044    }
1045}
1046
1047impl<T> AsRef<str> for Utf8Path<T>
1048where
1049    T: Utf8Encoding,
1050{
1051    #[inline]
1052    fn as_ref(&self) -> &str {
1053        &self.inner
1054    }
1055}
1056
1057impl<T> AsRef<Utf8Path<T>> for Utf8Path<T>
1058where
1059    T: Utf8Encoding,
1060{
1061    #[inline]
1062    fn as_ref(&self) -> &Utf8Path<T> {
1063        self
1064    }
1065}
1066
1067impl<T> AsRef<Utf8Path<T>> for str
1068where
1069    T: Utf8Encoding,
1070{
1071    #[inline]
1072    fn as_ref(&self) -> &Utf8Path<T> {
1073        Utf8Path::new(self)
1074    }
1075}
1076
1077impl<T> AsRef<Utf8Path<T>> for String
1078where
1079    T: Utf8Encoding,
1080{
1081    #[inline]
1082    fn as_ref(&self) -> &Utf8Path<T> {
1083        Utf8Path::new(self)
1084    }
1085}
1086
1087impl<T> fmt::Debug for Utf8Path<T>
1088where
1089    T: Utf8Encoding,
1090{
1091    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1092        f.debug_struct("Utf8Path")
1093            .field("_encoding", &T::label())
1094            .field("inner", &&self.inner)
1095            .finish()
1096    }
1097}
1098
1099impl<T> fmt::Display for Utf8Path<T>
1100where
1101    T: Utf8Encoding,
1102{
1103    /// Format path into a [`String`] using the underlying [`str`] representation.
1104    ///
1105    /// # Examples
1106    ///
1107    /// ```
1108    /// use typed_path::{Utf8Path, Utf8UnixEncoding};
1109    ///
1110    /// // NOTE: A path cannot be created on its own without a defined encoding
1111    /// let s = Utf8Path::<Utf8UnixEncoding>::new("foo.txt").to_string();
1112    /// assert_eq!(s, "foo.txt");
1113    /// ```
1114    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1115        fmt::Display::fmt(&self.inner, formatter)
1116    }
1117}
1118
1119impl<T> cmp::PartialEq for Utf8Path<T>
1120where
1121    T: Utf8Encoding,
1122{
1123    #[inline]
1124    fn eq(&self, other: &Utf8Path<T>) -> bool {
1125        self.components() == other.components()
1126    }
1127}
1128
1129impl<T> cmp::Eq for Utf8Path<T> where T: Utf8Encoding {}
1130
1131impl<T> Hash for Utf8Path<T>
1132where
1133    T: Utf8Encoding,
1134{
1135    fn hash<H: Hasher>(&self, h: &mut H) {
1136        T::hash(self.as_str(), h)
1137    }
1138}
1139
1140impl<T> cmp::PartialOrd for Utf8Path<T>
1141where
1142    T: Utf8Encoding,
1143{
1144    #[inline]
1145    fn partial_cmp(&self, other: &Utf8Path<T>) -> Option<cmp::Ordering> {
1146        Some(self.cmp(other))
1147    }
1148}
1149
1150impl<T> cmp::Ord for Utf8Path<T>
1151where
1152    T: Utf8Encoding,
1153{
1154    #[inline]
1155    fn cmp(&self, other: &Utf8Path<T>) -> cmp::Ordering {
1156        self.components().cmp(other.components())
1157    }
1158}
1159
1160impl<T> From<&Utf8Path<T>> for Box<Utf8Path<T>>
1161where
1162    T: Utf8Encoding,
1163{
1164    /// Creates a boxed [`Utf8Path`] from a reference.
1165    ///
1166    /// This will allocate and clone `path` to it.
1167    fn from(path: &Utf8Path<T>) -> Self {
1168        let boxed: Box<str> = path.inner.into();
1169        let rw = Box::into_raw(boxed) as *mut Utf8Path<T>;
1170        unsafe { Box::from_raw(rw) }
1171    }
1172}
1173
1174impl<T> From<Cow<'_, Utf8Path<T>>> for Box<Utf8Path<T>>
1175where
1176    T: Utf8Encoding,
1177{
1178    /// Creates a boxed [`Utf8Path`] from a clone-on-write pointer.
1179    ///
1180    /// Converting from a `Cow::Owned` does not clone or allocate.
1181    #[inline]
1182    fn from(cow: Cow<'_, Utf8Path<T>>) -> Box<Utf8Path<T>> {
1183        match cow {
1184            Cow::Borrowed(path) => Box::from(path),
1185            Cow::Owned(path) => Box::from(path),
1186        }
1187    }
1188}
1189
1190impl<T> From<Utf8PathBuf<T>> for Box<Utf8Path<T>>
1191where
1192    T: Utf8Encoding,
1193{
1194    /// Converts a [`Utf8PathBuf`] into a <code>[Box]&lt;[Utf8Path]&gt;</code>.
1195    ///
1196    /// This conversion currently should not allocate memory,
1197    /// but this behavior is not guaranteed on all platforms or in all future versions.
1198    #[inline]
1199    fn from(p: Utf8PathBuf<T>) -> Box<Utf8Path<T>> {
1200        p.into_boxed_path()
1201    }
1202}
1203
1204impl<'a, T> From<&'a Utf8Path<T>> for Cow<'a, Utf8Path<T>>
1205where
1206    T: Utf8Encoding,
1207{
1208    /// Creates a clone-on-write pointer from a reference to
1209    /// [`Utf8Path`].
1210    ///
1211    /// This conversion does not clone or allocate.
1212    #[inline]
1213    fn from(s: &'a Utf8Path<T>) -> Self {
1214        Cow::Borrowed(s)
1215    }
1216}
1217
1218impl<T> From<Utf8PathBuf<T>> for Cow<'_, Utf8Path<T>>
1219where
1220    T: Utf8Encoding,
1221{
1222    /// Creates a clone-on-write pointer from an owned
1223    /// instance of [`Utf8PathBuf`].
1224    ///
1225    /// This conversion does not clone or allocate.
1226    #[inline]
1227    fn from(s: Utf8PathBuf<T>) -> Self {
1228        Cow::Owned(s)
1229    }
1230}
1231
1232impl<'a, T> From<&'a Utf8PathBuf<T>> for Cow<'a, Utf8Path<T>>
1233where
1234    T: Utf8Encoding,
1235{
1236    /// Creates a clone-on-write pointer from a reference to
1237    /// [`Utf8PathBuf`].
1238    ///
1239    /// This conversion does not clone or allocate.
1240    #[inline]
1241    fn from(p: &'a Utf8PathBuf<T>) -> Self {
1242        Cow::Borrowed(p.as_path())
1243    }
1244}
1245
1246#[cfg(target_has_atomic = "ptr")]
1247impl<T> From<Utf8PathBuf<T>> for Arc<Utf8Path<T>>
1248where
1249    T: Utf8Encoding,
1250{
1251    /// Converts a [`Utf8PathBuf`] into an <code>[Arc]<[Utf8Path]></code> by moving the [`Utf8PathBuf`] data
1252    /// into a new [`Arc`] buffer.
1253    #[inline]
1254    fn from(path_buf: Utf8PathBuf<T>) -> Self {
1255        let arc: Arc<str> = Arc::from(path_buf.into_string());
1256        unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Utf8Path<T>) }
1257    }
1258}
1259
1260#[cfg(target_has_atomic = "ptr")]
1261impl<T> From<&Utf8Path<T>> for Arc<Utf8Path<T>>
1262where
1263    T: Utf8Encoding,
1264{
1265    /// Converts a [`Utf8Path`] into an [`Arc`] by copying the [`Utf8Path`] data into a new [`Arc`] buffer.
1266    #[inline]
1267    fn from(path: &Utf8Path<T>) -> Self {
1268        let arc: Arc<str> = Arc::from(path.as_str().to_string());
1269        unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Utf8Path<T>) }
1270    }
1271}
1272
1273impl<T> From<Utf8PathBuf<T>> for Rc<Utf8Path<T>>
1274where
1275    T: Utf8Encoding,
1276{
1277    /// Converts a [`Utf8PathBuf`] into an <code>[Rc]<[Utf8Path]></code> by moving the [`Utf8PathBuf`] data into
1278    /// a new [`Rc`] buffer.
1279    #[inline]
1280    fn from(path_buf: Utf8PathBuf<T>) -> Self {
1281        let rc: Rc<str> = Rc::from(path_buf.into_string());
1282        unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Utf8Path<T>) }
1283    }
1284}
1285
1286impl<T> From<&Utf8Path<T>> for Rc<Utf8Path<T>>
1287where
1288    T: Utf8Encoding,
1289{
1290    /// Converts a [`Utf8Path`] into an [`Rc`] by copying the [`Utf8Path`] data into a new [`Rc`] buffer.
1291    #[inline]
1292    fn from(path: &Utf8Path<T>) -> Self {
1293        let rc: Rc<str> = Rc::from(path.as_str());
1294        unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Utf8Path<T>) }
1295    }
1296}
1297
1298impl<'a, T> IntoIterator for &'a Utf8Path<T>
1299where
1300    T: Utf8Encoding,
1301{
1302    type IntoIter = Utf8Iter<'a, T>;
1303    type Item = &'a str;
1304
1305    #[inline]
1306    fn into_iter(self) -> Self::IntoIter {
1307        self.iter()
1308    }
1309}
1310
1311impl<T> ToOwned for Utf8Path<T>
1312where
1313    T: Utf8Encoding,
1314{
1315    type Owned = Utf8PathBuf<T>;
1316
1317    #[inline]
1318    fn to_owned(&self) -> Self::Owned {
1319        self.to_path_buf()
1320    }
1321}
1322
1323macro_rules! impl_cmp {
1324    ($($lt:lifetime),* ; $lhs:ty, $rhs: ty) => {
1325        impl<$($lt,)* T> PartialEq<$rhs> for $lhs
1326        where
1327            T: Utf8Encoding,
1328        {
1329            #[inline]
1330            fn eq(&self, other: &$rhs) -> bool {
1331                <Utf8Path<T> as PartialEq>::eq(self, other)
1332            }
1333        }
1334
1335        impl<$($lt,)* T> PartialEq<$lhs> for $rhs
1336        where
1337            T: Utf8Encoding,
1338        {
1339            #[inline]
1340            fn eq(&self, other: &$lhs) -> bool {
1341                <Utf8Path<T> as PartialEq>::eq(self, other)
1342            }
1343        }
1344
1345        impl<$($lt,)* T> PartialOrd<$rhs> for $lhs
1346        where
1347            T: Utf8Encoding,
1348        {
1349            #[inline]
1350            fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> {
1351                <Utf8Path<T> as PartialOrd>::partial_cmp(self, other)
1352            }
1353        }
1354
1355        impl<$($lt,)* T> PartialOrd<$lhs> for $rhs
1356        where
1357            T: Utf8Encoding,
1358        {
1359            #[inline]
1360            fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> {
1361                <Utf8Path<T> as PartialOrd>::partial_cmp(self, other)
1362            }
1363        }
1364    };
1365}
1366
1367impl_cmp!(; Utf8PathBuf<T>, Utf8Path<T>);
1368impl_cmp!('a; Utf8PathBuf<T>, &'a Utf8Path<T>);
1369impl_cmp!('a; Cow<'a, Utf8Path<T>>, Utf8Path<T>);
1370impl_cmp!('a, 'b; Cow<'a, Utf8Path<T>>, &'b Utf8Path<T>);
1371impl_cmp!('a; Cow<'a, Utf8Path<T>>, Utf8PathBuf<T>);
1372
1373macro_rules! impl_cmp_bytes {
1374    ($($lt:lifetime),* ; $lhs:ty, $rhs: ty) => {
1375        impl<$($lt,)* T> PartialEq<$rhs> for $lhs
1376        where
1377            T: Utf8Encoding,
1378        {
1379            #[inline]
1380            fn eq(&self, other: &$rhs) -> bool {
1381                <Utf8Path<T> as PartialEq>::eq(self, other.as_ref())
1382            }
1383        }
1384
1385        impl<$($lt,)* T> PartialEq<$lhs> for $rhs
1386        where
1387            T: Utf8Encoding,
1388        {
1389            #[inline]
1390            fn eq(&self, other: &$lhs) -> bool {
1391                <Utf8Path<T> as PartialEq>::eq(self.as_ref(), other)
1392            }
1393        }
1394
1395        impl<$($lt,)* T> PartialOrd<$rhs> for $lhs
1396        where
1397            T: Utf8Encoding,
1398        {
1399            #[inline]
1400            fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> {
1401                <Utf8Path<T> as PartialOrd>::partial_cmp(self, other.as_ref())
1402            }
1403        }
1404
1405        impl<$($lt,)* T> PartialOrd<$lhs> for $rhs
1406        where
1407            T: Utf8Encoding,
1408        {
1409            #[inline]
1410            fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> {
1411                <Utf8Path<T> as PartialOrd>::partial_cmp(self.as_ref(), other)
1412            }
1413        }
1414    };
1415}
1416
1417impl_cmp_bytes!(; Utf8PathBuf<T>, str);
1418impl_cmp_bytes!('a; Utf8PathBuf<T>, &'a str);
1419impl_cmp_bytes!(; Utf8PathBuf<T>, String);
1420impl_cmp_bytes!(; Utf8Path<T>, str);
1421impl_cmp_bytes!('a; Utf8Path<T>, &'a str);
1422impl_cmp_bytes!(; Utf8Path<T>, String);
1423impl_cmp_bytes!('a; &'a Utf8Path<T>, str);
1424impl_cmp_bytes!('a; &'a Utf8Path<T>, String);
1425
1426mod helpers {
1427    use super::*;
1428
1429    pub fn rsplit_file_at_dot(file: &str) -> (Option<&str>, Option<&str>) {
1430        if file == ".." {
1431            return (Some(file), None);
1432        }
1433
1434        let mut iter = file.rsplitn(2, '.');
1435        let after = iter.next();
1436        let before = iter.next();
1437        if before == Some("") {
1438            (Some(file), None)
1439        } else {
1440            (before, after)
1441        }
1442    }
1443
1444    // Iterate through `iter` while it matches `prefix`; return `None` if `prefix`
1445    // is not a prefix of `iter`, otherwise return `Some(iter_after_prefix)` giving
1446    // `iter` after having exhausted `prefix`.
1447    pub fn iter_after<'a, 'b, T, U, I, J>(mut iter: I, mut prefix: J) -> Option<I>
1448    where
1449        T: Utf8Component<'a>,
1450        U: Utf8Component<'b>,
1451        I: Iterator<Item = T> + Clone,
1452        J: Iterator<Item = U>,
1453    {
1454        loop {
1455            let mut iter_next = iter.clone();
1456            match (iter_next.next(), prefix.next()) {
1457                // TODO: Because there is not a `Component` struct, there is no direct comparison
1458                //       between T and U since they aren't the same type due to different
1459                //       lifetimes. We get around this with an equality check by converting these
1460                //       components to bytes, which should work for the Unix and Windows component
1461                //       implementations, but is error-prone as any new implementation could
1462                //       deviate in a way that breaks this subtlely. Instead, need to figure out
1463                //       either how to bring back equality of x == y WITHOUT needing to have
1464                //       T: PartialEq<U> because that causes lifetime problems for `strip_prefix`
1465                (Some(ref x), Some(ref y)) if x.as_str() == y.as_str() => (),
1466                (Some(_), Some(_)) => return None,
1467                (Some(_), None) => return Some(iter),
1468                (None, None) => return Some(iter),
1469                (None, Some(_)) => return None,
1470            }
1471            iter = iter_next;
1472        }
1473    }
1474}
1475
1476#[cfg(feature = "serde")]
1477struct Utf8PathVisitor<T>(PhantomData<T>)
1478where
1479    T: Utf8Encoding;
1480
1481#[cfg(feature = "serde")]
1482impl<'a, T> Visitor<'a> for Utf8PathVisitor<T>
1483where
1484    T: Utf8Encoding + 'a,
1485{
1486    type Value = &'a Utf8Path<T>;
1487
1488    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1489        formatter.write_str("a borrowed UTF-8 path")
1490    }
1491
1492    fn visit_borrowed_str<E>(self, v: &'a str) -> Result<Self::Value, E>
1493    where
1494        E: de::Error,
1495    {
1496        Ok(Utf8Path::new(v))
1497    }
1498
1499    fn visit_borrowed_bytes<E>(self, v: &'a [u8]) -> Result<Self::Value, E>
1500    where
1501        E: de::Error,
1502    {
1503        core::str::from_utf8(v)
1504            .map(str::as_ref)
1505            .map_err(|_| de::Error::invalid_value(Unexpected::Bytes(v), &self))
1506    }
1507}
1508
1509#[cfg(feature = "serde")]
1510impl<T> Serialize for Utf8Path<T>
1511where
1512    T: Utf8Encoding,
1513{
1514    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1515    where
1516        S: Serializer,
1517    {
1518        self.as_str().serialize(serializer)
1519    }
1520}
1521
1522#[cfg(feature = "serde")]
1523impl<'de: 'a, 'a, T> Deserialize<'de> for &'a Utf8Path<T>
1524where
1525    T: Utf8Encoding + 'de,
1526{
1527    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1528    where
1529        D: Deserializer<'de>,
1530    {
1531        deserializer.deserialize_str(Utf8PathVisitor(PhantomData))
1532    }
1533}
1534
1535#[cfg(feature = "serde")]
1536impl<'de, T> Deserialize<'de> for Box<Utf8Path<T>>
1537where
1538    T: Utf8Encoding,
1539{
1540    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1541    where
1542        D: Deserializer<'de>,
1543    {
1544        Deserialize::deserialize(deserializer).map(Utf8PathBuf::into_boxed_path)
1545    }
1546}
1547
1548#[cfg(any(
1549    unix,
1550    all(target_vendor = "fortanix", target_env = "sgx"),
1551    target_os = "solid_asp3",
1552    target_os = "hermit",
1553    all(
1554        target_os = "wasi",
1555        any(not(target_env = "p2"), feature = "nightly-wasip2")
1556    )
1557))]
1558#[cfg(feature = "std")]
1559mod std_conversions {
1560    use std::ffi::{OsStr, OsString};
1561    #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
1562    use std::os::fortanix_sgx as os;
1563    #[cfg(target_os = "solid_asp3")]
1564    use std::os::solid as os;
1565    #[cfg(any(target_os = "hermit", unix))]
1566    use std::os::unix as os;
1567    #[cfg(target_os = "wasi")]
1568    use std::os::wasi as os;
1569
1570    use os::ffi::OsStrExt;
1571
1572    use super::*;
1573    use crate::common::TryAsRef;
1574
1575    impl<T> TryAsRef<Utf8Path<T>> for OsStr
1576    where
1577        T: Utf8Encoding,
1578    {
1579        #[inline]
1580        fn try_as_ref(&self) -> Option<&Utf8Path<T>> {
1581            std::str::from_utf8(self.as_bytes()).ok().map(Utf8Path::new)
1582        }
1583    }
1584
1585    impl<T> TryAsRef<Utf8Path<T>> for OsString
1586    where
1587        T: Utf8Encoding,
1588    {
1589        #[inline]
1590        fn try_as_ref(&self) -> Option<&Utf8Path<T>> {
1591            std::str::from_utf8(self.as_bytes()).ok().map(Utf8Path::new)
1592        }
1593    }
1594
1595    impl<T> AsRef<OsStr> for Utf8Path<T>
1596    where
1597        T: Utf8Encoding,
1598    {
1599        #[inline]
1600        fn as_ref(&self) -> &OsStr {
1601            OsStrExt::from_bytes(self.as_str().as_bytes())
1602        }
1603    }
1604}
1605
1606#[cfg(all(test, feature = "serde"))]
1607mod tests {
1608    use crate::Utf8WindowsPath;
1609
1610    #[test]
1611    fn deserialize_invalid_utf8() {
1612        let bytes = vec![b'"', b'f', b'o', b'o', b'\\', 0xff, b'"'];
1613
1614        assert!(serde_json::from_slice::<&Utf8WindowsPath>(&bytes).is_err());
1615    }
1616}