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