typed_path/common/utf8/
path.rs

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