camino/
lib.rs

1// Copyright (c) The camino Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4#![warn(missing_docs)]
5#![cfg_attr(doc_cfg, feature(doc_cfg, doc_auto_cfg))]
6
7//! UTF-8 encoded paths.
8//!
9//! `camino` is an extension of the `std::path` module that adds new [`Utf8PathBuf`] and [`Utf8Path`]
10//! types. These are like the standard library's [`PathBuf`] and [`Path`] types, except they are
11//! guaranteed to only contain UTF-8 encoded data. Therefore, they expose the ability to get their
12//! contents as strings, they implement `Display`, etc.
13//!
14//! The `std::path` types are not guaranteed to be valid UTF-8. This is the right decision for the standard library,
15//! since it must be as general as possible. However, on all platforms, non-Unicode paths are vanishingly uncommon for a
16//! number of reasons:
17//! * Unicode won. There are still some legacy codebases that store paths in encodings like Shift-JIS, but most
18//!   have been converted to Unicode at this point.
19//! * Unicode is the common subset of supported paths across Windows and Unix platforms. (On Windows, Rust stores paths
20//!   as [an extension to UTF-8](https://simonsapin.github.io/wtf-8/), and converts them to UTF-16 at Win32
21//!   API boundaries.)
22//! * There are already many systems, such as Cargo, that only support UTF-8 paths. If your own tool interacts with any such
23//!   system, you can assume that paths are valid UTF-8 without creating any additional burdens on consumers.
24//! * The ["makefile problem"](https://www.mercurial-scm.org/wiki/EncodingStrategy#The_.22makefile_problem.22)
25//!   (which also applies to `Cargo.toml`, and any other metadata file that lists the names of other files) has *no general,
26//!   cross-platform solution* in systems that support non-UTF-8 paths. However, restricting paths to UTF-8 eliminates
27//!   this problem.
28//!
29//! Therefore, many programs that want to manipulate paths *do* assume they contain UTF-8 data, and convert them to `str`s
30//! as  necessary. However, because this invariant is not encoded in the `Path` type, conversions such as
31//! `path.to_str().unwrap()` need to be repeated again and again, creating a frustrating experience.
32//!
33//! Instead, `camino` allows you to check that your paths are UTF-8 *once*, and then manipulate them
34//! as valid UTF-8 from there on, avoiding repeated lossy and confusing conversions.
35
36// General note: we use #[allow(clippy::incompatible_msrv)] for code that's already guarded by a
37// version-specific cfg conditional.
38
39use std::{
40    borrow::{Borrow, Cow},
41    cmp::Ordering,
42    convert::{Infallible, TryFrom, TryInto},
43    error,
44    ffi::{OsStr, OsString},
45    fmt,
46    fs::{self, Metadata},
47    hash::{Hash, Hasher},
48    io,
49    iter::FusedIterator,
50    ops::Deref,
51    path::*,
52    rc::Rc,
53    str::FromStr,
54    sync::Arc,
55};
56
57#[cfg(feature = "proptest1")]
58mod proptest_impls;
59#[cfg(feature = "serde1")]
60mod serde_impls;
61#[cfg(test)]
62mod tests;
63
64/// An owned, mutable UTF-8 path (akin to [`String`]).
65///
66/// This type provides methods like [`push`] and [`set_extension`] that mutate
67/// the path in place. It also implements [`Deref`] to [`Utf8Path`], meaning that
68/// all methods on [`Utf8Path`] slices are available on `Utf8PathBuf` values as well.
69///
70/// [`push`]: Utf8PathBuf::push
71/// [`set_extension`]: Utf8PathBuf::set_extension
72///
73/// # Examples
74///
75/// You can use [`push`] to build up a `Utf8PathBuf` from
76/// components:
77///
78/// ```
79/// use camino::Utf8PathBuf;
80///
81/// let mut path = Utf8PathBuf::new();
82///
83/// path.push(r"C:\");
84/// path.push("windows");
85/// path.push("system32");
86///
87/// path.set_extension("dll");
88/// ```
89///
90/// However, [`push`] is best used for dynamic situations. This is a better way
91/// to do this when you know all of the components ahead of time:
92///
93/// ```
94/// use camino::Utf8PathBuf;
95///
96/// let path: Utf8PathBuf = [r"C:\", "windows", "system32.dll"].iter().collect();
97/// ```
98///
99/// We can still do better than this! Since these are all strings, we can use
100/// `From::from`:
101///
102/// ```
103/// use camino::Utf8PathBuf;
104///
105/// let path = Utf8PathBuf::from(r"C:\windows\system32.dll");
106/// ```
107///
108/// Which method works best depends on what kind of situation you're in.
109// NB: Internal PathBuf must only contain utf8 data
110#[derive(Clone, Default)]
111#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
112#[cfg_attr(feature = "serde1", serde(transparent))]
113#[repr(transparent)]
114pub struct Utf8PathBuf(PathBuf);
115
116impl Utf8PathBuf {
117    /// Allocates an empty `Utf8PathBuf`.
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// use camino::Utf8PathBuf;
123    ///
124    /// let path = Utf8PathBuf::new();
125    /// ```
126    #[must_use]
127    pub fn new() -> Utf8PathBuf {
128        Utf8PathBuf(PathBuf::new())
129    }
130
131    /// Creates a new `Utf8PathBuf` from a `PathBuf` containing valid UTF-8 characters.
132    ///
133    /// Errors with the original `PathBuf` if it is not valid UTF-8.
134    ///
135    /// For a version that returns a type that implements [`std::error::Error`], use the
136    /// `TryFrom<PathBuf>` impl.
137    ///
138    /// # Examples
139    ///
140    /// ```
141    /// use camino::Utf8PathBuf;
142    /// use std::ffi::OsStr;
143    /// # #[cfg(unix)]
144    /// use std::os::unix::ffi::OsStrExt;
145    /// use std::path::PathBuf;
146    ///
147    /// let unicode_path = PathBuf::from("/valid/unicode");
148    /// Utf8PathBuf::from_path_buf(unicode_path).expect("valid Unicode path succeeded");
149    ///
150    /// // Paths on Unix can be non-UTF-8.
151    /// # #[cfg(unix)]
152    /// let non_unicode_str = OsStr::from_bytes(b"\xFF\xFF\xFF");
153    /// # #[cfg(unix)]
154    /// let non_unicode_path = PathBuf::from(non_unicode_str);
155    /// # #[cfg(unix)]
156    /// Utf8PathBuf::from_path_buf(non_unicode_path).expect_err("non-Unicode path failed");
157    /// ```
158    pub fn from_path_buf(path: PathBuf) -> Result<Utf8PathBuf, PathBuf> {
159        match path.into_os_string().into_string() {
160            Ok(string) => Ok(Utf8PathBuf::from(string)),
161            Err(os_string) => Err(PathBuf::from(os_string)),
162        }
163    }
164
165    /// Converts a `Utf8PathBuf` to a [`PathBuf`].
166    ///
167    /// This is equivalent to the `From<Utf8PathBuf> for PathBuf` impl, but may aid in type
168    /// inference.
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// use camino::Utf8PathBuf;
174    /// use std::path::PathBuf;
175    ///
176    /// let utf8_path_buf = Utf8PathBuf::from("foo.txt");
177    /// let std_path_buf = utf8_path_buf.into_std_path_buf();
178    /// assert_eq!(std_path_buf.to_str(), Some("foo.txt"));
179    ///
180    /// // Convert back to a Utf8PathBuf.
181    /// let new_utf8_path_buf = Utf8PathBuf::from_path_buf(std_path_buf).unwrap();
182    /// assert_eq!(new_utf8_path_buf, "foo.txt");
183    /// ```
184    #[must_use = "`self` will be dropped if the result is not used"]
185    pub fn into_std_path_buf(self) -> PathBuf {
186        self.into()
187    }
188
189    /// Creates a new `Utf8PathBuf` with a given capacity used to create the internal [`PathBuf`].
190    /// See [`with_capacity`] defined on [`PathBuf`].
191    ///
192    /// *Requires Rust 1.44 or newer.*
193    ///
194    /// # Examples
195    ///
196    /// ```
197    /// use camino::Utf8PathBuf;
198    ///
199    /// let mut path = Utf8PathBuf::with_capacity(10);
200    /// let capacity = path.capacity();
201    ///
202    /// // This push is done without reallocating
203    /// path.push(r"C:\");
204    ///
205    /// assert_eq!(capacity, path.capacity());
206    /// ```
207    ///
208    /// [`with_capacity`]: PathBuf::with_capacity
209    #[cfg(path_buf_capacity)]
210    #[allow(clippy::incompatible_msrv)]
211    #[must_use]
212    pub fn with_capacity(capacity: usize) -> Utf8PathBuf {
213        Utf8PathBuf(PathBuf::with_capacity(capacity))
214    }
215
216    /// Coerces to a [`Utf8Path`] slice.
217    ///
218    /// # Examples
219    ///
220    /// ```
221    /// use camino::{Utf8Path, Utf8PathBuf};
222    ///
223    /// let p = Utf8PathBuf::from("/test");
224    /// assert_eq!(Utf8Path::new("/test"), p.as_path());
225    /// ```
226    #[must_use]
227    pub fn as_path(&self) -> &Utf8Path {
228        // SAFETY: every Utf8PathBuf constructor ensures that self is valid UTF-8
229        unsafe { Utf8Path::assume_utf8(&self.0) }
230    }
231
232    /// Extends `self` with `path`.
233    ///
234    /// If `path` is absolute, it replaces the current path.
235    ///
236    /// On Windows:
237    ///
238    /// * if `path` has a root but no prefix (e.g., `\windows`), it
239    ///   replaces everything except for the prefix (if any) of `self`.
240    /// * if `path` has a prefix but no root, it replaces `self`.
241    ///
242    /// # Examples
243    ///
244    /// Pushing a relative path extends the existing path:
245    ///
246    /// ```
247    /// use camino::Utf8PathBuf;
248    ///
249    /// let mut path = Utf8PathBuf::from("/tmp");
250    /// path.push("file.bk");
251    /// assert_eq!(path, Utf8PathBuf::from("/tmp/file.bk"));
252    /// ```
253    ///
254    /// Pushing an absolute path replaces the existing path:
255    ///
256    /// ```
257    /// use camino::Utf8PathBuf;
258    ///
259    /// let mut path = Utf8PathBuf::from("/tmp");
260    /// path.push("/etc");
261    /// assert_eq!(path, Utf8PathBuf::from("/etc"));
262    /// ```
263    pub fn push(&mut self, path: impl AsRef<Utf8Path>) {
264        self.0.push(&path.as_ref().0)
265    }
266
267    /// Truncates `self` to [`self.parent`].
268    ///
269    /// Returns `false` and does nothing if [`self.parent`] is [`None`].
270    /// Otherwise, returns `true`.
271    ///
272    /// [`self.parent`]: Utf8Path::parent
273    ///
274    /// # Examples
275    ///
276    /// ```
277    /// use camino::{Utf8Path, Utf8PathBuf};
278    ///
279    /// let mut p = Utf8PathBuf::from("/spirited/away.rs");
280    ///
281    /// p.pop();
282    /// assert_eq!(Utf8Path::new("/spirited"), p);
283    /// p.pop();
284    /// assert_eq!(Utf8Path::new("/"), p);
285    /// ```
286    pub fn pop(&mut self) -> bool {
287        self.0.pop()
288    }
289
290    /// Updates [`self.file_name`] to `file_name`.
291    ///
292    /// If [`self.file_name`] was [`None`], this is equivalent to pushing
293    /// `file_name`.
294    ///
295    /// Otherwise it is equivalent to calling [`pop`] and then pushing
296    /// `file_name`. The new path will be a sibling of the original path.
297    /// (That is, it will have the same parent.)
298    ///
299    /// [`self.file_name`]: Utf8Path::file_name
300    /// [`pop`]: Utf8PathBuf::pop
301    ///
302    /// # Examples
303    ///
304    /// ```
305    /// use camino::Utf8PathBuf;
306    ///
307    /// let mut buf = Utf8PathBuf::from("/");
308    /// assert_eq!(buf.file_name(), None);
309    /// buf.set_file_name("bar");
310    /// assert_eq!(buf, Utf8PathBuf::from("/bar"));
311    /// assert!(buf.file_name().is_some());
312    /// buf.set_file_name("baz.txt");
313    /// assert_eq!(buf, Utf8PathBuf::from("/baz.txt"));
314    /// ```
315    pub fn set_file_name(&mut self, file_name: impl AsRef<str>) {
316        self.0.set_file_name(file_name.as_ref())
317    }
318
319    /// Updates [`self.extension`] to `extension`.
320    ///
321    /// Returns `false` and does nothing if [`self.file_name`] is [`None`],
322    /// returns `true` and updates the extension otherwise.
323    ///
324    /// If [`self.extension`] is [`None`], the extension is added; otherwise
325    /// it is replaced.
326    ///
327    /// [`self.file_name`]: Utf8Path::file_name
328    /// [`self.extension`]: Utf8Path::extension
329    ///
330    /// # Examples
331    ///
332    /// ```
333    /// use camino::{Utf8Path, Utf8PathBuf};
334    ///
335    /// let mut p = Utf8PathBuf::from("/feel/the");
336    ///
337    /// p.set_extension("force");
338    /// assert_eq!(Utf8Path::new("/feel/the.force"), p.as_path());
339    ///
340    /// p.set_extension("dark_side");
341    /// assert_eq!(Utf8Path::new("/feel/the.dark_side"), p.as_path());
342    /// ```
343    pub fn set_extension(&mut self, extension: impl AsRef<str>) -> bool {
344        self.0.set_extension(extension.as_ref())
345    }
346
347    /// Consumes the `Utf8PathBuf`, yielding its internal [`String`] storage.
348    ///
349    /// # Examples
350    ///
351    /// ```
352    /// use camino::Utf8PathBuf;
353    ///
354    /// let p = Utf8PathBuf::from("/the/head");
355    /// let s = p.into_string();
356    /// assert_eq!(s, "/the/head");
357    /// ```
358    #[must_use = "`self` will be dropped if the result is not used"]
359    pub fn into_string(self) -> String {
360        self.into_os_string().into_string().unwrap()
361    }
362
363    /// Consumes the `Utf8PathBuf`, yielding its internal [`OsString`] storage.
364    ///
365    /// # Examples
366    ///
367    /// ```
368    /// use camino::Utf8PathBuf;
369    /// use std::ffi::OsStr;
370    ///
371    /// let p = Utf8PathBuf::from("/the/head");
372    /// let s = p.into_os_string();
373    /// assert_eq!(s, OsStr::new("/the/head"));
374    /// ```
375    #[must_use = "`self` will be dropped if the result is not used"]
376    pub fn into_os_string(self) -> OsString {
377        self.0.into_os_string()
378    }
379
380    /// Converts this `Utf8PathBuf` into a [boxed](Box) [`Utf8Path`].
381    #[must_use = "`self` will be dropped if the result is not used"]
382    pub fn into_boxed_path(self) -> Box<Utf8Path> {
383        let ptr = Box::into_raw(self.0.into_boxed_path()) as *mut Utf8Path;
384        // SAFETY:
385        // * self is valid UTF-8
386        // * ptr was constructed by consuming self so it represents an owned path
387        // * Utf8Path is marked as #[repr(transparent)] so the conversion from *mut Path to
388        //   *mut Utf8Path is valid
389        unsafe { Box::from_raw(ptr) }
390    }
391
392    /// Invokes [`capacity`] on the underlying instance of [`PathBuf`].
393    ///
394    /// *Requires Rust 1.44 or newer.*
395    ///
396    /// [`capacity`]: PathBuf::capacity
397    #[cfg(path_buf_capacity)]
398    #[allow(clippy::incompatible_msrv)]
399    #[must_use]
400    pub fn capacity(&self) -> usize {
401        self.0.capacity()
402    }
403
404    /// Invokes [`clear`] on the underlying instance of [`PathBuf`].
405    ///
406    /// *Requires Rust 1.44 or newer.*
407    ///
408    /// [`clear`]: PathBuf::clear
409    #[cfg(path_buf_capacity)]
410    #[allow(clippy::incompatible_msrv)]
411    pub fn clear(&mut self) {
412        self.0.clear()
413    }
414
415    /// Invokes [`reserve`] on the underlying instance of [`PathBuf`].
416    ///
417    /// *Requires Rust 1.44 or newer.*
418    ///
419    /// [`reserve`]: PathBuf::reserve
420    #[cfg(path_buf_capacity)]
421    #[allow(clippy::incompatible_msrv)]
422    pub fn reserve(&mut self, additional: usize) {
423        self.0.reserve(additional)
424    }
425
426    /// Invokes [`try_reserve`] on the underlying instance of [`PathBuf`].
427    ///
428    /// *Requires Rust 1.63 or newer.*
429    ///
430    /// [`try_reserve`]: PathBuf::try_reserve
431    #[cfg(try_reserve_2)]
432    #[allow(clippy::incompatible_msrv)]
433    #[inline]
434    pub fn try_reserve(
435        &mut self,
436        additional: usize,
437    ) -> Result<(), std::collections::TryReserveError> {
438        self.0.try_reserve(additional)
439    }
440
441    /// Invokes [`reserve_exact`] on the underlying instance of [`PathBuf`].
442    ///
443    /// *Requires Rust 1.44 or newer.*
444    ///
445    /// [`reserve_exact`]: PathBuf::reserve_exact
446    #[cfg(path_buf_capacity)]
447    #[allow(clippy::incompatible_msrv)]
448    pub fn reserve_exact(&mut self, additional: usize) {
449        self.0.reserve_exact(additional)
450    }
451
452    /// Invokes [`try_reserve_exact`] on the underlying instance of [`PathBuf`].
453    ///
454    /// *Requires Rust 1.63 or newer.*
455    ///
456    /// [`try_reserve_exact`]: PathBuf::try_reserve_exact
457    #[cfg(try_reserve_2)]
458    #[allow(clippy::incompatible_msrv)]
459    #[inline]
460    pub fn try_reserve_exact(
461        &mut self,
462        additional: usize,
463    ) -> Result<(), std::collections::TryReserveError> {
464        self.0.try_reserve_exact(additional)
465    }
466
467    /// Invokes [`shrink_to_fit`] on the underlying instance of [`PathBuf`].
468    ///
469    /// *Requires Rust 1.44 or newer.*
470    ///
471    /// [`shrink_to_fit`]: PathBuf::shrink_to_fit
472    #[cfg(path_buf_capacity)]
473    #[allow(clippy::incompatible_msrv)]
474    pub fn shrink_to_fit(&mut self) {
475        self.0.shrink_to_fit()
476    }
477
478    /// Invokes [`shrink_to`] on the underlying instance of [`PathBuf`].
479    ///
480    /// *Requires Rust 1.56 or newer.*
481    ///
482    /// [`shrink_to`]: PathBuf::shrink_to
483    #[cfg(shrink_to)]
484    #[allow(clippy::incompatible_msrv)]
485    #[inline]
486    pub fn shrink_to(&mut self, min_capacity: usize) {
487        self.0.shrink_to(min_capacity)
488    }
489}
490
491impl Deref for Utf8PathBuf {
492    type Target = Utf8Path;
493
494    fn deref(&self) -> &Utf8Path {
495        self.as_path()
496    }
497}
498
499/// *Requires Rust 1.68 or newer.*
500#[cfg(path_buf_deref_mut)]
501#[allow(clippy::incompatible_msrv)]
502impl std::ops::DerefMut for Utf8PathBuf {
503    fn deref_mut(&mut self) -> &mut Self::Target {
504        unsafe { Utf8Path::assume_utf8_mut(&mut self.0) }
505    }
506}
507
508impl fmt::Debug for Utf8PathBuf {
509    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
510        fmt::Debug::fmt(&**self, f)
511    }
512}
513
514impl fmt::Display for Utf8PathBuf {
515    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
516        fmt::Display::fmt(self.as_str(), f)
517    }
518}
519
520impl<P: AsRef<Utf8Path>> Extend<P> for Utf8PathBuf {
521    fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
522        for path in iter {
523            self.push(path);
524        }
525    }
526}
527
528/// A slice of a UTF-8 path (akin to [`str`]).
529///
530/// This type supports a number of operations for inspecting a path, including
531/// breaking the path into its components (separated by `/` on Unix and by either
532/// `/` or `\` on Windows), extracting the file name, determining whether the path
533/// is absolute, and so on.
534///
535/// This is an *unsized* type, meaning that it must always be used behind a
536/// pointer like `&` or [`Box`]. For an owned version of this type,
537/// see [`Utf8PathBuf`].
538///
539/// # Examples
540///
541/// ```
542/// use camino::Utf8Path;
543///
544/// // Note: this example does work on Windows
545/// let path = Utf8Path::new("./foo/bar.txt");
546///
547/// let parent = path.parent();
548/// assert_eq!(parent, Some(Utf8Path::new("./foo")));
549///
550/// let file_stem = path.file_stem();
551/// assert_eq!(file_stem, Some("bar"));
552///
553/// let extension = path.extension();
554/// assert_eq!(extension, Some("txt"));
555/// ```
556// NB: Internal Path must only contain utf8 data
557#[repr(transparent)]
558pub struct Utf8Path(Path);
559
560impl Utf8Path {
561    /// Directly wraps a string slice as a `Utf8Path` slice.
562    ///
563    /// This is a cost-free conversion.
564    ///
565    /// # Examples
566    ///
567    /// ```
568    /// use camino::Utf8Path;
569    ///
570    /// Utf8Path::new("foo.txt");
571    /// ```
572    ///
573    /// You can create `Utf8Path`s from `String`s, or even other `Utf8Path`s:
574    ///
575    /// ```
576    /// use camino::Utf8Path;
577    ///
578    /// let string = String::from("foo.txt");
579    /// let from_string = Utf8Path::new(&string);
580    /// let from_path = Utf8Path::new(&from_string);
581    /// assert_eq!(from_string, from_path);
582    /// ```
583    pub fn new(s: &(impl AsRef<str> + ?Sized)) -> &Utf8Path {
584        let path = Path::new(s.as_ref());
585        // SAFETY: s is a str which means it is always valid UTF-8
586        unsafe { Utf8Path::assume_utf8(path) }
587    }
588
589    /// Converts a [`Path`] to a `Utf8Path`.
590    ///
591    /// Returns `None` if the path is not valid UTF-8.
592    ///
593    /// For a version that returns a type that implements [`std::error::Error`], use the
594    /// [`TryFrom<&Path>`][tryfrom] impl.
595    ///
596    /// [tryfrom]: #impl-TryFrom<%26'a+Path>-for-%26'a+Utf8Path
597    ///
598    /// # Examples
599    ///
600    /// ```
601    /// use camino::Utf8Path;
602    /// use std::ffi::OsStr;
603    /// # #[cfg(unix)]
604    /// use std::os::unix::ffi::OsStrExt;
605    /// use std::path::Path;
606    ///
607    /// let unicode_path = Path::new("/valid/unicode");
608    /// Utf8Path::from_path(unicode_path).expect("valid Unicode path succeeded");
609    ///
610    /// // Paths on Unix can be non-UTF-8.
611    /// # #[cfg(unix)]
612    /// let non_unicode_str = OsStr::from_bytes(b"\xFF\xFF\xFF");
613    /// # #[cfg(unix)]
614    /// let non_unicode_path = Path::new(non_unicode_str);
615    /// # #[cfg(unix)]
616    /// assert!(Utf8Path::from_path(non_unicode_path).is_none(), "non-Unicode path failed");
617    /// ```
618    pub fn from_path(path: &Path) -> Option<&Utf8Path> {
619        path.as_os_str().to_str().map(Utf8Path::new)
620    }
621
622    /// Converts a `Utf8Path` to a [`Path`].
623    ///
624    /// This is equivalent to the `AsRef<&Path> for &Utf8Path` impl, but may aid in type inference.
625    ///
626    /// # Examples
627    ///
628    /// ```
629    /// use camino::Utf8Path;
630    /// use std::path::Path;
631    ///
632    /// let utf8_path = Utf8Path::new("foo.txt");
633    /// let std_path: &Path = utf8_path.as_std_path();
634    /// assert_eq!(std_path.to_str(), Some("foo.txt"));
635    ///
636    /// // Convert back to a Utf8Path.
637    /// let new_utf8_path = Utf8Path::from_path(std_path).unwrap();
638    /// assert_eq!(new_utf8_path, "foo.txt");
639    /// ```
640    #[inline]
641    pub fn as_std_path(&self) -> &Path {
642        self.as_ref()
643    }
644
645    /// Yields the underlying [`str`] slice.
646    ///
647    /// Unlike [`Path::to_str`], this always returns a slice because the contents of a `Utf8Path`
648    /// are guaranteed to be valid UTF-8.
649    ///
650    /// # Examples
651    ///
652    /// ```
653    /// use camino::Utf8Path;
654    ///
655    /// let s = Utf8Path::new("foo.txt").as_str();
656    /// assert_eq!(s, "foo.txt");
657    /// ```
658    ///
659    /// [`str`]: str
660    #[inline]
661    #[must_use]
662    pub fn as_str(&self) -> &str {
663        // SAFETY: every Utf8Path constructor ensures that self is valid UTF-8
664        unsafe { str_assume_utf8(self.as_os_str()) }
665    }
666
667    /// Yields the underlying [`OsStr`] slice.
668    ///
669    /// # Examples
670    ///
671    /// ```
672    /// use camino::Utf8Path;
673    ///
674    /// let os_str = Utf8Path::new("foo.txt").as_os_str();
675    /// assert_eq!(os_str, std::ffi::OsStr::new("foo.txt"));
676    /// ```
677    #[inline]
678    #[must_use]
679    pub fn as_os_str(&self) -> &OsStr {
680        self.0.as_os_str()
681    }
682
683    /// Converts a `Utf8Path` to an owned [`Utf8PathBuf`].
684    ///
685    /// # Examples
686    ///
687    /// ```
688    /// use camino::{Utf8Path, Utf8PathBuf};
689    ///
690    /// let path_buf = Utf8Path::new("foo.txt").to_path_buf();
691    /// assert_eq!(path_buf, Utf8PathBuf::from("foo.txt"));
692    /// ```
693    #[inline]
694    #[must_use = "this returns the result of the operation, \
695                  without modifying the original"]
696    pub fn to_path_buf(&self) -> Utf8PathBuf {
697        Utf8PathBuf(self.0.to_path_buf())
698    }
699
700    /// Returns `true` if the `Utf8Path` is absolute, i.e., if it is independent of
701    /// the current directory.
702    ///
703    /// * On Unix, a path is absolute if it starts with the root, so
704    ///   `is_absolute` and [`has_root`] are equivalent.
705    ///
706    /// * On Windows, a path is absolute if it has a prefix and starts with the
707    ///   root: `c:\windows` is absolute, while `c:temp` and `\temp` are not.
708    ///
709    /// # Examples
710    ///
711    /// ```
712    /// use camino::Utf8Path;
713    ///
714    /// assert!(!Utf8Path::new("foo.txt").is_absolute());
715    /// ```
716    ///
717    /// [`has_root`]: Utf8Path::has_root
718    #[inline]
719    #[must_use]
720    pub fn is_absolute(&self) -> bool {
721        self.0.is_absolute()
722    }
723
724    /// Returns `true` if the `Utf8Path` is relative, i.e., not absolute.
725    ///
726    /// See [`is_absolute`]'s documentation for more details.
727    ///
728    /// # Examples
729    ///
730    /// ```
731    /// use camino::Utf8Path;
732    ///
733    /// assert!(Utf8Path::new("foo.txt").is_relative());
734    /// ```
735    ///
736    /// [`is_absolute`]: Utf8Path::is_absolute
737    #[inline]
738    #[must_use]
739    pub fn is_relative(&self) -> bool {
740        self.0.is_relative()
741    }
742
743    /// Returns `true` if the `Utf8Path` has a root.
744    ///
745    /// * On Unix, a path has a root if it begins with `/`.
746    ///
747    /// * On Windows, a path has a root if it:
748    ///     * has no prefix and begins with a separator, e.g., `\windows`
749    ///     * has a prefix followed by a separator, e.g., `c:\windows` but not `c:windows`
750    ///     * has any non-disk prefix, e.g., `\\server\share`
751    ///
752    /// # Examples
753    ///
754    /// ```
755    /// use camino::Utf8Path;
756    ///
757    /// assert!(Utf8Path::new("/etc/passwd").has_root());
758    /// ```
759    #[inline]
760    #[must_use]
761    pub fn has_root(&self) -> bool {
762        self.0.has_root()
763    }
764
765    /// Returns the `Path` without its final component, if there is one.
766    ///
767    /// Returns [`None`] if the path terminates in a root or prefix.
768    ///
769    /// # Examples
770    ///
771    /// ```
772    /// use camino::Utf8Path;
773    ///
774    /// let path = Utf8Path::new("/foo/bar");
775    /// let parent = path.parent().unwrap();
776    /// assert_eq!(parent, Utf8Path::new("/foo"));
777    ///
778    /// let grand_parent = parent.parent().unwrap();
779    /// assert_eq!(grand_parent, Utf8Path::new("/"));
780    /// assert_eq!(grand_parent.parent(), None);
781    /// ```
782    #[inline]
783    #[must_use]
784    pub fn parent(&self) -> Option<&Utf8Path> {
785        self.0.parent().map(|path| {
786            // SAFETY: self is valid UTF-8, so parent is valid UTF-8 as well
787            unsafe { Utf8Path::assume_utf8(path) }
788        })
789    }
790
791    /// Produces an iterator over `Utf8Path` and its ancestors.
792    ///
793    /// The iterator will yield the `Utf8Path` that is returned if the [`parent`] method is used zero
794    /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`,
795    /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns
796    /// [`None`], the iterator will do likewise. The iterator will always yield at least one value,
797    /// namely `&self`.
798    ///
799    /// # Examples
800    ///
801    /// ```
802    /// use camino::Utf8Path;
803    ///
804    /// let mut ancestors = Utf8Path::new("/foo/bar").ancestors();
805    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("/foo/bar")));
806    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("/foo")));
807    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("/")));
808    /// assert_eq!(ancestors.next(), None);
809    ///
810    /// let mut ancestors = Utf8Path::new("../foo/bar").ancestors();
811    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("../foo/bar")));
812    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("../foo")));
813    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("..")));
814    /// assert_eq!(ancestors.next(), Some(Utf8Path::new("")));
815    /// assert_eq!(ancestors.next(), None);
816    /// ```
817    ///
818    /// [`parent`]: Utf8Path::parent
819    #[inline]
820    pub fn ancestors(&self) -> Utf8Ancestors<'_> {
821        Utf8Ancestors(self.0.ancestors())
822    }
823
824    /// Returns the final component of the `Utf8Path`, if there is one.
825    ///
826    /// If the path is a normal file, this is the file name. If it's the path of a directory, this
827    /// is the directory name.
828    ///
829    /// Returns [`None`] if the path terminates in `..`.
830    ///
831    /// # Examples
832    ///
833    /// ```
834    /// use camino::Utf8Path;
835    ///
836    /// assert_eq!(Some("bin"), Utf8Path::new("/usr/bin/").file_name());
837    /// assert_eq!(Some("foo.txt"), Utf8Path::new("tmp/foo.txt").file_name());
838    /// assert_eq!(Some("foo.txt"), Utf8Path::new("foo.txt/.").file_name());
839    /// assert_eq!(Some("foo.txt"), Utf8Path::new("foo.txt/.//").file_name());
840    /// assert_eq!(None, Utf8Path::new("foo.txt/..").file_name());
841    /// assert_eq!(None, Utf8Path::new("/").file_name());
842    /// ```
843    #[inline]
844    #[must_use]
845    pub fn file_name(&self) -> Option<&str> {
846        self.0.file_name().map(|s| {
847            // SAFETY: self is valid UTF-8, so file_name is valid UTF-8 as well
848            unsafe { str_assume_utf8(s) }
849        })
850    }
851
852    /// Returns a path that, when joined onto `base`, yields `self`.
853    ///
854    /// # Errors
855    ///
856    /// If `base` is not a prefix of `self` (i.e., [`starts_with`]
857    /// returns `false`), returns [`Err`].
858    ///
859    /// [`starts_with`]: Utf8Path::starts_with
860    ///
861    /// # Examples
862    ///
863    /// ```
864    /// use camino::{Utf8Path, Utf8PathBuf};
865    ///
866    /// let path = Utf8Path::new("/test/haha/foo.txt");
867    ///
868    /// assert_eq!(path.strip_prefix("/"), Ok(Utf8Path::new("test/haha/foo.txt")));
869    /// assert_eq!(path.strip_prefix("/test"), Ok(Utf8Path::new("haha/foo.txt")));
870    /// assert_eq!(path.strip_prefix("/test/"), Ok(Utf8Path::new("haha/foo.txt")));
871    /// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Utf8Path::new("")));
872    /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Utf8Path::new("")));
873    ///
874    /// assert!(path.strip_prefix("test").is_err());
875    /// assert!(path.strip_prefix("/haha").is_err());
876    ///
877    /// let prefix = Utf8PathBuf::from("/test/");
878    /// assert_eq!(path.strip_prefix(prefix), Ok(Utf8Path::new("haha/foo.txt")));
879    /// ```
880    #[inline]
881    pub fn strip_prefix(&self, base: impl AsRef<Path>) -> Result<&Utf8Path, StripPrefixError> {
882        self.0.strip_prefix(base).map(|path| {
883            // SAFETY: self is valid UTF-8, and strip_prefix returns a part of self (or an empty
884            // string), so it is valid UTF-8 as well.
885            unsafe { Utf8Path::assume_utf8(path) }
886        })
887    }
888
889    /// Determines whether `base` is a prefix of `self`.
890    ///
891    /// Only considers whole path components to match.
892    ///
893    /// # Examples
894    ///
895    /// ```
896    /// use camino::Utf8Path;
897    ///
898    /// let path = Utf8Path::new("/etc/passwd");
899    ///
900    /// assert!(path.starts_with("/etc"));
901    /// assert!(path.starts_with("/etc/"));
902    /// assert!(path.starts_with("/etc/passwd"));
903    /// assert!(path.starts_with("/etc/passwd/")); // extra slash is okay
904    /// assert!(path.starts_with("/etc/passwd///")); // multiple extra slashes are okay
905    ///
906    /// assert!(!path.starts_with("/e"));
907    /// assert!(!path.starts_with("/etc/passwd.txt"));
908    ///
909    /// assert!(!Utf8Path::new("/etc/foo.rs").starts_with("/etc/foo"));
910    /// ```
911    #[inline]
912    #[must_use]
913    pub fn starts_with(&self, base: impl AsRef<Path>) -> bool {
914        self.0.starts_with(base)
915    }
916
917    /// Determines whether `child` is a suffix of `self`.
918    ///
919    /// Only considers whole path components to match.
920    ///
921    /// # Examples
922    ///
923    /// ```
924    /// use camino::Utf8Path;
925    ///
926    /// let path = Utf8Path::new("/etc/resolv.conf");
927    ///
928    /// assert!(path.ends_with("resolv.conf"));
929    /// assert!(path.ends_with("etc/resolv.conf"));
930    /// assert!(path.ends_with("/etc/resolv.conf"));
931    ///
932    /// assert!(!path.ends_with("/resolv.conf"));
933    /// assert!(!path.ends_with("conf")); // use .extension() instead
934    /// ```
935    #[inline]
936    #[must_use]
937    pub fn ends_with(&self, base: impl AsRef<Path>) -> bool {
938        self.0.ends_with(base)
939    }
940
941    /// Extracts the stem (non-extension) portion of [`self.file_name`].
942    ///
943    /// [`self.file_name`]: Utf8Path::file_name
944    ///
945    /// The stem is:
946    ///
947    /// * [`None`], if there is no file name;
948    /// * The entire file name if there is no embedded `.`;
949    /// * The entire file name if the file name begins with `.` and has no other `.`s within;
950    /// * Otherwise, the portion of the file name before the final `.`
951    ///
952    /// # Examples
953    ///
954    /// ```
955    /// use camino::Utf8Path;
956    ///
957    /// assert_eq!("foo", Utf8Path::new("foo.rs").file_stem().unwrap());
958    /// assert_eq!("foo.tar", Utf8Path::new("foo.tar.gz").file_stem().unwrap());
959    /// ```
960    #[inline]
961    #[must_use]
962    pub fn file_stem(&self) -> Option<&str> {
963        self.0.file_stem().map(|s| {
964            // SAFETY: self is valid UTF-8, so file_stem is valid UTF-8 as well
965            unsafe { str_assume_utf8(s) }
966        })
967    }
968
969    /// Extracts the extension of [`self.file_name`], if possible.
970    ///
971    /// The extension is:
972    ///
973    /// * [`None`], if there is no file name;
974    /// * [`None`], if there is no embedded `.`;
975    /// * [`None`], if the file name begins with `.` and has no other `.`s within;
976    /// * Otherwise, the portion of the file name after the final `.`
977    ///
978    /// [`self.file_name`]: Utf8Path::file_name
979    ///
980    /// # Examples
981    ///
982    /// ```
983    /// use camino::Utf8Path;
984    ///
985    /// assert_eq!("rs", Utf8Path::new("foo.rs").extension().unwrap());
986    /// assert_eq!("gz", Utf8Path::new("foo.tar.gz").extension().unwrap());
987    /// ```
988    #[inline]
989    #[must_use]
990    pub fn extension(&self) -> Option<&str> {
991        self.0.extension().map(|s| {
992            // SAFETY: self is valid UTF-8, so extension is valid UTF-8 as well
993            unsafe { str_assume_utf8(s) }
994        })
995    }
996
997    /// Creates an owned [`Utf8PathBuf`] with `path` adjoined to `self`.
998    ///
999    /// See [`Utf8PathBuf::push`] for more details on what it means to adjoin a path.
1000    ///
1001    /// # Examples
1002    ///
1003    /// ```
1004    /// use camino::{Utf8Path, Utf8PathBuf};
1005    ///
1006    /// assert_eq!(Utf8Path::new("/etc").join("passwd"), Utf8PathBuf::from("/etc/passwd"));
1007    /// ```
1008    #[inline]
1009    #[must_use]
1010    pub fn join(&self, path: impl AsRef<Utf8Path>) -> Utf8PathBuf {
1011        Utf8PathBuf(self.0.join(&path.as_ref().0))
1012    }
1013
1014    /// Creates an owned [`PathBuf`] with `path` adjoined to `self`.
1015    ///
1016    /// See [`PathBuf::push`] for more details on what it means to adjoin a path.
1017    ///
1018    /// # Examples
1019    ///
1020    /// ```
1021    /// use camino::Utf8Path;
1022    /// use std::path::PathBuf;
1023    ///
1024    /// assert_eq!(Utf8Path::new("/etc").join_os("passwd"), PathBuf::from("/etc/passwd"));
1025    /// ```
1026    #[inline]
1027    #[must_use]
1028    pub fn join_os(&self, path: impl AsRef<Path>) -> PathBuf {
1029        self.0.join(path)
1030    }
1031
1032    /// Creates an owned [`Utf8PathBuf`] like `self` but with the given file name.
1033    ///
1034    /// See [`Utf8PathBuf::set_file_name`] for more details.
1035    ///
1036    /// # Examples
1037    ///
1038    /// ```
1039    /// use camino::{Utf8Path, Utf8PathBuf};
1040    ///
1041    /// let path = Utf8Path::new("/tmp/foo.txt");
1042    /// assert_eq!(path.with_file_name("bar.txt"), Utf8PathBuf::from("/tmp/bar.txt"));
1043    ///
1044    /// let path = Utf8Path::new("/tmp");
1045    /// assert_eq!(path.with_file_name("var"), Utf8PathBuf::from("/var"));
1046    /// ```
1047    #[inline]
1048    #[must_use]
1049    pub fn with_file_name(&self, file_name: impl AsRef<str>) -> Utf8PathBuf {
1050        Utf8PathBuf(self.0.with_file_name(file_name.as_ref()))
1051    }
1052
1053    /// Creates an owned [`Utf8PathBuf`] like `self` but with the given extension.
1054    ///
1055    /// See [`Utf8PathBuf::set_extension`] for more details.
1056    ///
1057    /// # Examples
1058    ///
1059    /// ```
1060    /// use camino::{Utf8Path, Utf8PathBuf};
1061    ///
1062    /// let path = Utf8Path::new("foo.rs");
1063    /// assert_eq!(path.with_extension("txt"), Utf8PathBuf::from("foo.txt"));
1064    ///
1065    /// let path = Utf8Path::new("foo.tar.gz");
1066    /// assert_eq!(path.with_extension(""), Utf8PathBuf::from("foo.tar"));
1067    /// assert_eq!(path.with_extension("xz"), Utf8PathBuf::from("foo.tar.xz"));
1068    /// assert_eq!(path.with_extension("").with_extension("txt"), Utf8PathBuf::from("foo.txt"));
1069    /// ```
1070    #[inline]
1071    pub fn with_extension(&self, extension: impl AsRef<str>) -> Utf8PathBuf {
1072        Utf8PathBuf(self.0.with_extension(extension.as_ref()))
1073    }
1074
1075    /// Produces an iterator over the [`Utf8Component`]s of the path.
1076    ///
1077    /// When parsing the path, there is a small amount of normalization:
1078    ///
1079    /// * Repeated separators are ignored, so `a/b` and `a//b` both have
1080    ///   `a` and `b` as components.
1081    ///
1082    /// * Occurrences of `.` are normalized away, except if they are at the
1083    ///   beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and
1084    ///   `a/b` all have `a` and `b` as components, but `./a/b` starts with
1085    ///   an additional [`CurDir`] component.
1086    ///
1087    /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent.
1088    ///
1089    /// Note that no other normalization takes place; in particular, `a/c`
1090    /// and `a/b/../c` are distinct, to account for the possibility that `b`
1091    /// is a symbolic link (so its parent isn't `a`).
1092    ///
1093    /// # Examples
1094    ///
1095    /// ```
1096    /// use camino::{Utf8Component, Utf8Path};
1097    ///
1098    /// let mut components = Utf8Path::new("/tmp/foo.txt").components();
1099    ///
1100    /// assert_eq!(components.next(), Some(Utf8Component::RootDir));
1101    /// assert_eq!(components.next(), Some(Utf8Component::Normal("tmp")));
1102    /// assert_eq!(components.next(), Some(Utf8Component::Normal("foo.txt")));
1103    /// assert_eq!(components.next(), None)
1104    /// ```
1105    ///
1106    /// [`CurDir`]: Utf8Component::CurDir
1107    #[inline]
1108    pub fn components(&self) -> Utf8Components {
1109        Utf8Components(self.0.components())
1110    }
1111
1112    /// Produces an iterator over the path's components viewed as [`str`]
1113    /// slices.
1114    ///
1115    /// For more information about the particulars of how the path is separated
1116    /// into components, see [`components`].
1117    ///
1118    /// [`components`]: Utf8Path::components
1119    ///
1120    /// # Examples
1121    ///
1122    /// ```
1123    /// use camino::Utf8Path;
1124    ///
1125    /// let mut it = Utf8Path::new("/tmp/foo.txt").iter();
1126    /// assert_eq!(it.next(), Some(std::path::MAIN_SEPARATOR.to_string().as_str()));
1127    /// assert_eq!(it.next(), Some("tmp"));
1128    /// assert_eq!(it.next(), Some("foo.txt"));
1129    /// assert_eq!(it.next(), None)
1130    /// ```
1131    #[inline]
1132    pub fn iter(&self) -> Iter<'_> {
1133        Iter {
1134            inner: self.components(),
1135        }
1136    }
1137
1138    /// Queries the file system to get information about a file, directory, etc.
1139    ///
1140    /// This function will traverse symbolic links to query information about the
1141    /// destination file.
1142    ///
1143    /// This is an alias to [`fs::metadata`].
1144    ///
1145    /// # Examples
1146    ///
1147    /// ```no_run
1148    /// use camino::Utf8Path;
1149    ///
1150    /// let path = Utf8Path::new("/Minas/tirith");
1151    /// let metadata = path.metadata().expect("metadata call failed");
1152    /// println!("{:?}", metadata.file_type());
1153    /// ```
1154    #[inline]
1155    pub fn metadata(&self) -> io::Result<fs::Metadata> {
1156        self.0.metadata()
1157    }
1158
1159    /// Queries the metadata about a file without following symlinks.
1160    ///
1161    /// This is an alias to [`fs::symlink_metadata`].
1162    ///
1163    /// # Examples
1164    ///
1165    /// ```no_run
1166    /// use camino::Utf8Path;
1167    ///
1168    /// let path = Utf8Path::new("/Minas/tirith");
1169    /// let metadata = path.symlink_metadata().expect("symlink_metadata call failed");
1170    /// println!("{:?}", metadata.file_type());
1171    /// ```
1172    #[inline]
1173    pub fn symlink_metadata(&self) -> io::Result<fs::Metadata> {
1174        self.0.symlink_metadata()
1175    }
1176
1177    /// Returns the canonical, absolute form of the path with all intermediate
1178    /// components normalized and symbolic links resolved.
1179    ///
1180    /// This returns a [`PathBuf`] because even if a symlink is valid Unicode, its target may not
1181    /// be. For a version that returns a [`Utf8PathBuf`], see
1182    /// [`canonicalize_utf8`](Self::canonicalize_utf8).
1183    ///
1184    /// This is an alias to [`fs::canonicalize`].
1185    ///
1186    /// # Examples
1187    ///
1188    /// ```no_run
1189    /// use camino::Utf8Path;
1190    /// use std::path::PathBuf;
1191    ///
1192    /// let path = Utf8Path::new("/foo/test/../test/bar.rs");
1193    /// assert_eq!(path.canonicalize().unwrap(), PathBuf::from("/foo/test/bar.rs"));
1194    /// ```
1195    #[inline]
1196    pub fn canonicalize(&self) -> io::Result<PathBuf> {
1197        self.0.canonicalize()
1198    }
1199
1200    /// Returns the canonical, absolute form of the path with all intermediate
1201    /// components normalized and symbolic links resolved.
1202    ///
1203    /// This method attempts to convert the resulting [`PathBuf`] into a [`Utf8PathBuf`]. For a
1204    /// version that does not attempt to do this conversion, see
1205    /// [`canonicalize`](Self::canonicalize).
1206    ///
1207    /// # Errors
1208    ///
1209    /// The I/O operation may return an error: see the [`fs::canonicalize`]
1210    /// documentation for more.
1211    ///
1212    /// If the resulting path is not UTF-8, an [`io::Error`] is returned with the
1213    /// [`ErrorKind`](io::ErrorKind) set to `InvalidData` and the payload set to a
1214    /// [`FromPathBufError`].
1215    ///
1216    /// # Examples
1217    ///
1218    /// ```no_run
1219    /// use camino::{Utf8Path, Utf8PathBuf};
1220    ///
1221    /// let path = Utf8Path::new("/foo/test/../test/bar.rs");
1222    /// assert_eq!(path.canonicalize_utf8().unwrap(), Utf8PathBuf::from("/foo/test/bar.rs"));
1223    /// ```
1224    pub fn canonicalize_utf8(&self) -> io::Result<Utf8PathBuf> {
1225        self.canonicalize()
1226            .and_then(|path| path.try_into().map_err(FromPathBufError::into_io_error))
1227    }
1228
1229    /// Reads a symbolic link, returning the file that the link points to.
1230    ///
1231    /// This returns a [`PathBuf`] because even if a symlink is valid Unicode, its target may not
1232    /// be. For a version that returns a [`Utf8PathBuf`], see
1233    /// [`read_link_utf8`](Self::read_link_utf8).
1234    ///
1235    /// This is an alias to [`fs::read_link`].
1236    ///
1237    /// # Examples
1238    ///
1239    /// ```no_run
1240    /// use camino::Utf8Path;
1241    ///
1242    /// let path = Utf8Path::new("/laputa/sky_castle.rs");
1243    /// let path_link = path.read_link().expect("read_link call failed");
1244    /// ```
1245    #[inline]
1246    pub fn read_link(&self) -> io::Result<PathBuf> {
1247        self.0.read_link()
1248    }
1249
1250    /// Reads a symbolic link, returning the file that the link points to.
1251    ///
1252    /// This method attempts to convert the resulting [`PathBuf`] into a [`Utf8PathBuf`]. For a
1253    /// version that does not attempt to do this conversion, see [`read_link`](Self::read_link).
1254    ///
1255    /// # Errors
1256    ///
1257    /// The I/O operation may return an error: see the [`fs::read_link`]
1258    /// documentation for more.
1259    ///
1260    /// If the resulting path is not UTF-8, an [`io::Error`] is returned with the
1261    /// [`ErrorKind`](io::ErrorKind) set to `InvalidData` and the payload set to a
1262    /// [`FromPathBufError`].
1263    ///
1264    /// # Examples
1265    ///
1266    /// ```no_run
1267    /// use camino::Utf8Path;
1268    ///
1269    /// let path = Utf8Path::new("/laputa/sky_castle.rs");
1270    /// let path_link = path.read_link_utf8().expect("read_link call failed");
1271    /// ```
1272    pub fn read_link_utf8(&self) -> io::Result<Utf8PathBuf> {
1273        self.read_link()
1274            .and_then(|path| path.try_into().map_err(FromPathBufError::into_io_error))
1275    }
1276
1277    /// Returns an iterator over the entries within a directory.
1278    ///
1279    /// The iterator will yield instances of [`io::Result`]`<`[`fs::DirEntry`]`>`. New
1280    /// errors may be encountered after an iterator is initially constructed.
1281    ///
1282    /// This is an alias to [`fs::read_dir`].
1283    ///
1284    /// # Examples
1285    ///
1286    /// ```no_run
1287    /// use camino::Utf8Path;
1288    ///
1289    /// let path = Utf8Path::new("/laputa");
1290    /// for entry in path.read_dir().expect("read_dir call failed") {
1291    ///     if let Ok(entry) = entry {
1292    ///         println!("{:?}", entry.path());
1293    ///     }
1294    /// }
1295    /// ```
1296    #[inline]
1297    pub fn read_dir(&self) -> io::Result<fs::ReadDir> {
1298        self.0.read_dir()
1299    }
1300
1301    /// Returns an iterator over the entries within a directory.
1302    ///
1303    /// The iterator will yield instances of [`io::Result`]`<`[`Utf8DirEntry`]`>`. New
1304    /// errors may be encountered after an iterator is initially constructed.
1305    ///
1306    /// # Errors
1307    ///
1308    /// The I/O operation may return an error: see the [`fs::read_dir`]
1309    /// documentation for more.
1310    ///
1311    /// If a directory entry is not UTF-8, an [`io::Error`] is returned with the
1312    /// [`ErrorKind`](io::ErrorKind) set to `InvalidData` and the payload set to a
1313    /// [`FromPathBufError`].
1314    ///
1315    /// # Examples
1316    ///
1317    /// ```no_run
1318    /// use camino::Utf8Path;
1319    ///
1320    /// let path = Utf8Path::new("/laputa");
1321    /// for entry in path.read_dir_utf8().expect("read_dir call failed") {
1322    ///     if let Ok(entry) = entry {
1323    ///         println!("{}", entry.path());
1324    ///     }
1325    /// }
1326    /// ```
1327    #[inline]
1328    pub fn read_dir_utf8(&self) -> io::Result<ReadDirUtf8> {
1329        self.0.read_dir().map(|inner| ReadDirUtf8 { inner })
1330    }
1331
1332    /// Returns `true` if the path points at an existing entity.
1333    ///
1334    /// Warning: this method may be error-prone, consider using [`try_exists()`] instead!
1335    /// It also has a risk of introducing time-of-check to time-of-use (TOCTOU) bugs.
1336    ///
1337    /// This function will traverse symbolic links to query information about the
1338    /// destination file. In case of broken symbolic links this will return `false`.
1339    ///
1340    /// If you cannot access the directory containing the file, e.g., because of a
1341    /// permission error, this will return `false`.
1342    ///
1343    /// # Examples
1344    ///
1345    /// ```no_run
1346    /// use camino::Utf8Path;
1347    /// assert!(!Utf8Path::new("does_not_exist.txt").exists());
1348    /// ```
1349    ///
1350    /// # See Also
1351    ///
1352    /// This is a convenience function that coerces errors to false. If you want to
1353    /// check errors, call [`fs::metadata`].
1354    ///
1355    /// [`try_exists()`]: Self::try_exists
1356    #[must_use]
1357    #[inline]
1358    pub fn exists(&self) -> bool {
1359        self.0.exists()
1360    }
1361
1362    /// Returns `Ok(true)` if the path points at an existing entity.
1363    ///
1364    /// This function will traverse symbolic links to query information about the
1365    /// destination file. In case of broken symbolic links this will return `Ok(false)`.
1366    ///
1367    /// As opposed to the [`exists()`] method, this one doesn't silently ignore errors
1368    /// unrelated to the path not existing. (E.g. it will return `Err(_)` in case of permission
1369    /// denied on some of the parent directories.)
1370    ///
1371    /// Note that while this avoids some pitfalls of the `exists()` method, it still can not
1372    /// prevent time-of-check to time-of-use (TOCTOU) bugs. You should only use it in scenarios
1373    /// where those bugs are not an issue.
1374    ///
1375    /// # Examples
1376    ///
1377    /// ```no_run
1378    /// use camino::Utf8Path;
1379    /// assert!(!Utf8Path::new("does_not_exist.txt").try_exists().expect("Can't check existence of file does_not_exist.txt"));
1380    /// assert!(Utf8Path::new("/root/secret_file.txt").try_exists().is_err());
1381    /// ```
1382    ///
1383    /// [`exists()`]: Self::exists
1384    #[inline]
1385    pub fn try_exists(&self) -> io::Result<bool> {
1386        // Note: this block is written this way rather than with a pattern guard to appease Rust
1387        // 1.34.
1388        match fs::metadata(self) {
1389            Ok(_) => Ok(true),
1390            Err(error) => {
1391                if error.kind() == io::ErrorKind::NotFound {
1392                    Ok(false)
1393                } else {
1394                    Err(error)
1395                }
1396            }
1397        }
1398    }
1399
1400    /// Returns `true` if the path exists on disk and is pointing at a regular file.
1401    ///
1402    /// This function will traverse symbolic links to query information about the
1403    /// destination file. In case of broken symbolic links this will return `false`.
1404    ///
1405    /// If you cannot access the directory containing the file, e.g., because of a
1406    /// permission error, this will return `false`.
1407    ///
1408    /// # Examples
1409    ///
1410    /// ```no_run
1411    /// use camino::Utf8Path;
1412    /// assert_eq!(Utf8Path::new("./is_a_directory/").is_file(), false);
1413    /// assert_eq!(Utf8Path::new("a_file.txt").is_file(), true);
1414    /// ```
1415    ///
1416    /// # See Also
1417    ///
1418    /// This is a convenience function that coerces errors to false. If you want to
1419    /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call
1420    /// [`fs::Metadata::is_file`] if it was [`Ok`].
1421    ///
1422    /// When the goal is simply to read from (or write to) the source, the most
1423    /// reliable way to test the source can be read (or written to) is to open
1424    /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on
1425    /// a Unix-like system for example. See [`fs::File::open`] or
1426    /// [`fs::OpenOptions::open`] for more information.
1427    #[must_use]
1428    #[inline]
1429    pub fn is_file(&self) -> bool {
1430        self.0.is_file()
1431    }
1432
1433    /// Returns `true` if the path exists on disk and is pointing at a directory.
1434    ///
1435    /// This function will traverse symbolic links to query information about the
1436    /// destination file. In case of broken symbolic links this will return `false`.
1437    ///
1438    /// If you cannot access the directory containing the file, e.g., because of a
1439    /// permission error, this will return `false`.
1440    ///
1441    /// # Examples
1442    ///
1443    /// ```no_run
1444    /// use camino::Utf8Path;
1445    /// assert_eq!(Utf8Path::new("./is_a_directory/").is_dir(), true);
1446    /// assert_eq!(Utf8Path::new("a_file.txt").is_dir(), false);
1447    /// ```
1448    ///
1449    /// # See Also
1450    ///
1451    /// This is a convenience function that coerces errors to false. If you want to
1452    /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call
1453    /// [`fs::Metadata::is_dir`] if it was [`Ok`].
1454    #[must_use]
1455    #[inline]
1456    pub fn is_dir(&self) -> bool {
1457        self.0.is_dir()
1458    }
1459
1460    /// Returns `true` if the path exists on disk and is pointing at a symbolic link.
1461    ///
1462    /// This function will not traverse symbolic links.
1463    /// In case of a broken symbolic link this will also return true.
1464    ///
1465    /// If you cannot access the directory containing the file, e.g., because of a
1466    /// permission error, this will return false.
1467    ///
1468    /// # Examples
1469    ///
1470    #[cfg_attr(unix, doc = "```no_run")]
1471    #[cfg_attr(not(unix), doc = "```ignore")]
1472    /// use camino::Utf8Path;
1473    /// use std::os::unix::fs::symlink;
1474    ///
1475    /// let link_path = Utf8Path::new("link");
1476    /// symlink("/origin_does_not_exist/", link_path).unwrap();
1477    /// assert_eq!(link_path.is_symlink(), true);
1478    /// assert_eq!(link_path.exists(), false);
1479    /// ```
1480    ///
1481    /// # See Also
1482    ///
1483    /// This is a convenience function that coerces errors to false. If you want to
1484    /// check errors, call [`Utf8Path::symlink_metadata`] and handle its [`Result`]. Then call
1485    /// [`fs::Metadata::is_symlink`] if it was [`Ok`].
1486    #[must_use]
1487    pub fn is_symlink(&self) -> bool {
1488        self.symlink_metadata()
1489            .map(|m| m.file_type().is_symlink())
1490            .unwrap_or(false)
1491    }
1492
1493    /// Converts a `Box<Utf8Path>` into a [`Utf8PathBuf`] without copying or allocating.
1494    #[must_use = "`self` will be dropped if the result is not used"]
1495    #[inline]
1496    pub fn into_path_buf(self: Box<Utf8Path>) -> Utf8PathBuf {
1497        let ptr = Box::into_raw(self) as *mut Path;
1498        // SAFETY:
1499        // * self is valid UTF-8
1500        // * ptr was constructed by consuming self so it represents an owned path.
1501        // * Utf8Path is marked as #[repr(transparent)] so the conversion from a *mut Utf8Path to a
1502        //   *mut Path is valid.
1503        let boxed_path = unsafe { Box::from_raw(ptr) };
1504        Utf8PathBuf(boxed_path.into_path_buf())
1505    }
1506
1507    // invariant: Path must be guaranteed to be utf-8 data
1508    #[inline]
1509    unsafe fn assume_utf8(path: &Path) -> &Utf8Path {
1510        // SAFETY: Utf8Path is marked as #[repr(transparent)] so the conversion from a
1511        // *const Path to a *const Utf8Path is valid.
1512        &*(path as *const Path as *const Utf8Path)
1513    }
1514
1515    #[cfg(path_buf_deref_mut)]
1516    #[inline]
1517    unsafe fn assume_utf8_mut(path: &mut Path) -> &mut Utf8Path {
1518        &mut *(path as *mut Path as *mut Utf8Path)
1519    }
1520}
1521
1522impl Clone for Box<Utf8Path> {
1523    fn clone(&self) -> Self {
1524        let boxed: Box<Path> = self.0.into();
1525        let ptr = Box::into_raw(boxed) as *mut Utf8Path;
1526        // SAFETY:
1527        // * self is valid UTF-8
1528        // * ptr was created by consuming a Box<Path> so it represents an rced pointer
1529        // * Utf8Path is marked as #[repr(transparent)] so the conversion from *mut Path to
1530        //   *mut Utf8Path is valid
1531        unsafe { Box::from_raw(ptr) }
1532    }
1533}
1534
1535impl fmt::Display for Utf8Path {
1536    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1537        fmt::Display::fmt(self.as_str(), f)
1538    }
1539}
1540
1541impl fmt::Debug for Utf8Path {
1542    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1543        fmt::Debug::fmt(self.as_str(), f)
1544    }
1545}
1546
1547/// An iterator over [`Utf8Path`] and its ancestors.
1548///
1549/// This `struct` is created by the [`ancestors`] method on [`Utf8Path`].
1550/// See its documentation for more.
1551///
1552/// # Examples
1553///
1554/// ```
1555/// use camino::Utf8Path;
1556///
1557/// let path = Utf8Path::new("/foo/bar");
1558///
1559/// for ancestor in path.ancestors() {
1560///     println!("{}", ancestor);
1561/// }
1562/// ```
1563///
1564/// [`ancestors`]: Utf8Path::ancestors
1565#[derive(Copy, Clone)]
1566#[must_use = "iterators are lazy and do nothing unless consumed"]
1567#[repr(transparent)]
1568pub struct Utf8Ancestors<'a>(Ancestors<'a>);
1569
1570impl<'a> fmt::Debug for Utf8Ancestors<'a> {
1571    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1572        fmt::Debug::fmt(&self.0, f)
1573    }
1574}
1575
1576impl<'a> Iterator for Utf8Ancestors<'a> {
1577    type Item = &'a Utf8Path;
1578
1579    #[inline]
1580    fn next(&mut self) -> Option<Self::Item> {
1581        self.0.next().map(|path| {
1582            // SAFETY: Utf8Ancestors was constructed from a Utf8Path, so it is guaranteed to
1583            // be valid UTF-8
1584            unsafe { Utf8Path::assume_utf8(path) }
1585        })
1586    }
1587}
1588
1589impl<'a> FusedIterator for Utf8Ancestors<'a> {}
1590
1591/// An iterator over the [`Utf8Component`]s of a [`Utf8Path`].
1592///
1593/// This `struct` is created by the [`components`] method on [`Utf8Path`].
1594/// See its documentation for more.
1595///
1596/// # Examples
1597///
1598/// ```
1599/// use camino::Utf8Path;
1600///
1601/// let path = Utf8Path::new("/tmp/foo/bar.txt");
1602///
1603/// for component in path.components() {
1604///     println!("{:?}", component);
1605/// }
1606/// ```
1607///
1608/// [`components`]: Utf8Path::components
1609#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
1610#[must_use = "iterators are lazy and do nothing unless consumed"]
1611pub struct Utf8Components<'a>(Components<'a>);
1612
1613impl<'a> Utf8Components<'a> {
1614    /// Extracts a slice corresponding to the portion of the path remaining for iteration.
1615    ///
1616    /// # Examples
1617    ///
1618    /// ```
1619    /// use camino::Utf8Path;
1620    ///
1621    /// let mut components = Utf8Path::new("/tmp/foo/bar.txt").components();
1622    /// components.next();
1623    /// components.next();
1624    ///
1625    /// assert_eq!(Utf8Path::new("foo/bar.txt"), components.as_path());
1626    /// ```
1627    #[must_use]
1628    #[inline]
1629    pub fn as_path(&self) -> &'a Utf8Path {
1630        // SAFETY: Utf8Components was constructed from a Utf8Path, so it is guaranteed to be valid
1631        // UTF-8
1632        unsafe { Utf8Path::assume_utf8(self.0.as_path()) }
1633    }
1634}
1635
1636impl<'a> Iterator for Utf8Components<'a> {
1637    type Item = Utf8Component<'a>;
1638
1639    #[inline]
1640    fn next(&mut self) -> Option<Self::Item> {
1641        self.0.next().map(|component| {
1642            // SAFETY: Utf8Component was constructed from a Utf8Path, so it is guaranteed to be
1643            // valid UTF-8
1644            unsafe { Utf8Component::new(component) }
1645        })
1646    }
1647}
1648
1649impl<'a> FusedIterator for Utf8Components<'a> {}
1650
1651impl<'a> DoubleEndedIterator for Utf8Components<'a> {
1652    #[inline]
1653    fn next_back(&mut self) -> Option<Self::Item> {
1654        self.0.next_back().map(|component| {
1655            // SAFETY: Utf8Component was constructed from a Utf8Path, so it is guaranteed to be
1656            // valid UTF-8
1657            unsafe { Utf8Component::new(component) }
1658        })
1659    }
1660}
1661
1662impl<'a> fmt::Debug for Utf8Components<'a> {
1663    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1664        fmt::Debug::fmt(&self.0, f)
1665    }
1666}
1667
1668impl AsRef<Utf8Path> for Utf8Components<'_> {
1669    #[inline]
1670    fn as_ref(&self) -> &Utf8Path {
1671        self.as_path()
1672    }
1673}
1674
1675impl AsRef<Path> for Utf8Components<'_> {
1676    #[inline]
1677    fn as_ref(&self) -> &Path {
1678        self.as_path().as_ref()
1679    }
1680}
1681
1682impl AsRef<str> for Utf8Components<'_> {
1683    #[inline]
1684    fn as_ref(&self) -> &str {
1685        self.as_path().as_ref()
1686    }
1687}
1688
1689impl AsRef<OsStr> for Utf8Components<'_> {
1690    #[inline]
1691    fn as_ref(&self) -> &OsStr {
1692        self.as_path().as_os_str()
1693    }
1694}
1695
1696/// An iterator over the [`Utf8Component`]s of a [`Utf8Path`], as [`str`] slices.
1697///
1698/// This `struct` is created by the [`iter`] method on [`Utf8Path`].
1699/// See its documentation for more.
1700///
1701/// [`iter`]: Utf8Path::iter
1702#[derive(Clone)]
1703#[must_use = "iterators are lazy and do nothing unless consumed"]
1704pub struct Iter<'a> {
1705    inner: Utf8Components<'a>,
1706}
1707
1708impl fmt::Debug for Iter<'_> {
1709    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1710        struct DebugHelper<'a>(&'a Utf8Path);
1711
1712        impl fmt::Debug for DebugHelper<'_> {
1713            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1714                f.debug_list().entries(self.0.iter()).finish()
1715            }
1716        }
1717
1718        f.debug_tuple("Iter")
1719            .field(&DebugHelper(self.as_path()))
1720            .finish()
1721    }
1722}
1723
1724impl<'a> Iter<'a> {
1725    /// Extracts a slice corresponding to the portion of the path remaining for iteration.
1726    ///
1727    /// # Examples
1728    ///
1729    /// ```
1730    /// use camino::Utf8Path;
1731    ///
1732    /// let mut iter = Utf8Path::new("/tmp/foo/bar.txt").iter();
1733    /// iter.next();
1734    /// iter.next();
1735    ///
1736    /// assert_eq!(Utf8Path::new("foo/bar.txt"), iter.as_path());
1737    /// ```
1738    #[must_use]
1739    #[inline]
1740    pub fn as_path(&self) -> &'a Utf8Path {
1741        self.inner.as_path()
1742    }
1743}
1744
1745impl AsRef<Utf8Path> for Iter<'_> {
1746    #[inline]
1747    fn as_ref(&self) -> &Utf8Path {
1748        self.as_path()
1749    }
1750}
1751
1752impl AsRef<Path> for Iter<'_> {
1753    #[inline]
1754    fn as_ref(&self) -> &Path {
1755        self.as_path().as_ref()
1756    }
1757}
1758
1759impl AsRef<str> for Iter<'_> {
1760    #[inline]
1761    fn as_ref(&self) -> &str {
1762        self.as_path().as_ref()
1763    }
1764}
1765
1766impl AsRef<OsStr> for Iter<'_> {
1767    #[inline]
1768    fn as_ref(&self) -> &OsStr {
1769        self.as_path().as_os_str()
1770    }
1771}
1772
1773impl<'a> Iterator for Iter<'a> {
1774    type Item = &'a str;
1775
1776    #[inline]
1777    fn next(&mut self) -> Option<&'a str> {
1778        self.inner.next().map(|component| component.as_str())
1779    }
1780}
1781
1782impl<'a> DoubleEndedIterator for Iter<'a> {
1783    #[inline]
1784    fn next_back(&mut self) -> Option<&'a str> {
1785        self.inner.next_back().map(|component| component.as_str())
1786    }
1787}
1788
1789impl FusedIterator for Iter<'_> {}
1790
1791/// A single component of a path.
1792///
1793/// A `Utf8Component` roughly corresponds to a substring between path separators
1794/// (`/` or `\`).
1795///
1796/// This `enum` is created by iterating over [`Utf8Components`], which in turn is
1797/// created by the [`components`](Utf8Path::components) method on [`Utf8Path`].
1798///
1799/// # Examples
1800///
1801/// ```rust
1802/// use camino::{Utf8Component, Utf8Path};
1803///
1804/// let path = Utf8Path::new("/tmp/foo/bar.txt");
1805/// let components = path.components().collect::<Vec<_>>();
1806/// assert_eq!(&components, &[
1807///     Utf8Component::RootDir,
1808///     Utf8Component::Normal("tmp"),
1809///     Utf8Component::Normal("foo"),
1810///     Utf8Component::Normal("bar.txt"),
1811/// ]);
1812/// ```
1813#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
1814pub enum Utf8Component<'a> {
1815    /// A Windows path prefix, e.g., `C:` or `\\server\share`.
1816    ///
1817    /// There is a large variety of prefix types, see [`Utf8Prefix`]'s documentation
1818    /// for more.
1819    ///
1820    /// Does not occur on Unix.
1821    Prefix(Utf8PrefixComponent<'a>),
1822
1823    /// The root directory component, appears after any prefix and before anything else.
1824    ///
1825    /// It represents a separator that designates that a path starts from root.
1826    RootDir,
1827
1828    /// A reference to the current directory, i.e., `.`.
1829    CurDir,
1830
1831    /// A reference to the parent directory, i.e., `..`.
1832    ParentDir,
1833
1834    /// A normal component, e.g., `a` and `b` in `a/b`.
1835    ///
1836    /// This variant is the most common one, it represents references to files
1837    /// or directories.
1838    Normal(&'a str),
1839}
1840
1841impl<'a> Utf8Component<'a> {
1842    unsafe fn new(component: Component<'a>) -> Utf8Component<'a> {
1843        match component {
1844            Component::Prefix(prefix) => Utf8Component::Prefix(Utf8PrefixComponent(prefix)),
1845            Component::RootDir => Utf8Component::RootDir,
1846            Component::CurDir => Utf8Component::CurDir,
1847            Component::ParentDir => Utf8Component::ParentDir,
1848            Component::Normal(s) => Utf8Component::Normal(str_assume_utf8(s)),
1849        }
1850    }
1851
1852    /// Extracts the underlying [`str`] slice.
1853    ///
1854    /// # Examples
1855    ///
1856    /// ```
1857    /// use camino::Utf8Path;
1858    ///
1859    /// let path = Utf8Path::new("./tmp/foo/bar.txt");
1860    /// let components: Vec<_> = path.components().map(|comp| comp.as_str()).collect();
1861    /// assert_eq!(&components, &[".", "tmp", "foo", "bar.txt"]);
1862    /// ```
1863    #[must_use]
1864    #[inline]
1865    pub fn as_str(&self) -> &'a str {
1866        // SAFETY: Utf8Component was constructed from a Utf8Path, so it is guaranteed to be
1867        // valid UTF-8
1868        unsafe { str_assume_utf8(self.as_os_str()) }
1869    }
1870
1871    /// Extracts the underlying [`OsStr`] slice.
1872    ///
1873    /// # Examples
1874    ///
1875    /// ```
1876    /// use camino::Utf8Path;
1877    ///
1878    /// let path = Utf8Path::new("./tmp/foo/bar.txt");
1879    /// let components: Vec<_> = path.components().map(|comp| comp.as_os_str()).collect();
1880    /// assert_eq!(&components, &[".", "tmp", "foo", "bar.txt"]);
1881    /// ```
1882    #[must_use]
1883    pub fn as_os_str(&self) -> &'a OsStr {
1884        match *self {
1885            Utf8Component::Prefix(prefix) => prefix.as_os_str(),
1886            Utf8Component::RootDir => Component::RootDir.as_os_str(),
1887            Utf8Component::CurDir => Component::CurDir.as_os_str(),
1888            Utf8Component::ParentDir => Component::ParentDir.as_os_str(),
1889            Utf8Component::Normal(s) => OsStr::new(s),
1890        }
1891    }
1892}
1893
1894impl<'a> fmt::Debug for Utf8Component<'a> {
1895    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1896        fmt::Debug::fmt(self.as_os_str(), f)
1897    }
1898}
1899
1900impl<'a> fmt::Display for Utf8Component<'a> {
1901    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1902        fmt::Display::fmt(self.as_str(), f)
1903    }
1904}
1905
1906impl AsRef<Utf8Path> for Utf8Component<'_> {
1907    #[inline]
1908    fn as_ref(&self) -> &Utf8Path {
1909        self.as_str().as_ref()
1910    }
1911}
1912
1913impl AsRef<Path> for Utf8Component<'_> {
1914    #[inline]
1915    fn as_ref(&self) -> &Path {
1916        self.as_os_str().as_ref()
1917    }
1918}
1919
1920impl AsRef<str> for Utf8Component<'_> {
1921    #[inline]
1922    fn as_ref(&self) -> &str {
1923        self.as_str()
1924    }
1925}
1926
1927impl AsRef<OsStr> for Utf8Component<'_> {
1928    #[inline]
1929    fn as_ref(&self) -> &OsStr {
1930        self.as_os_str()
1931    }
1932}
1933
1934/// Windows path prefixes, e.g., `C:` or `\\server\share`.
1935///
1936/// Windows uses a variety of path prefix styles, including references to drive
1937/// volumes (like `C:`), network shared folders (like `\\server\share`), and
1938/// others. In addition, some path prefixes are "verbatim" (i.e., prefixed with
1939/// `\\?\`), in which case `/` is *not* treated as a separator and essentially
1940/// no normalization is performed.
1941///
1942/// # Examples
1943///
1944/// ```
1945/// use camino::{Utf8Component, Utf8Path, Utf8Prefix};
1946/// use camino::Utf8Prefix::*;
1947///
1948/// fn get_path_prefix(s: &str) -> Utf8Prefix {
1949///     let path = Utf8Path::new(s);
1950///     match path.components().next().unwrap() {
1951///         Utf8Component::Prefix(prefix_component) => prefix_component.kind(),
1952///         _ => panic!(),
1953///     }
1954/// }
1955///
1956/// # if cfg!(windows) {
1957/// assert_eq!(Verbatim("pictures"), get_path_prefix(r"\\?\pictures\kittens"));
1958/// assert_eq!(VerbatimUNC("server", "share"), get_path_prefix(r"\\?\UNC\server\share"));
1959/// assert_eq!(VerbatimDisk(b'C'), get_path_prefix(r"\\?\c:\"));
1960/// assert_eq!(DeviceNS("BrainInterface"), get_path_prefix(r"\\.\BrainInterface"));
1961/// assert_eq!(UNC("server", "share"), get_path_prefix(r"\\server\share"));
1962/// assert_eq!(Disk(b'C'), get_path_prefix(r"C:\Users\Rust\Pictures\Ferris"));
1963/// # }
1964/// ```
1965#[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
1966pub enum Utf8Prefix<'a> {
1967    /// Verbatim prefix, e.g., `\\?\cat_pics`.
1968    ///
1969    /// Verbatim prefixes consist of `\\?\` immediately followed by the given
1970    /// component.
1971    Verbatim(&'a str),
1972
1973    /// Verbatim prefix using Windows' _**U**niform **N**aming **C**onvention_,
1974    /// e.g., `\\?\UNC\server\share`.
1975    ///
1976    /// Verbatim UNC prefixes consist of `\\?\UNC\` immediately followed by the
1977    /// server's hostname and a share name.
1978    VerbatimUNC(&'a str, &'a str),
1979
1980    /// Verbatim disk prefix, e.g., `\\?\C:`.
1981    ///
1982    /// Verbatim disk prefixes consist of `\\?\` immediately followed by the
1983    /// drive letter and `:`.
1984    VerbatimDisk(u8),
1985
1986    /// Device namespace prefix, e.g., `\\.\COM42`.
1987    ///
1988    /// Device namespace prefixes consist of `\\.\` immediately followed by the
1989    /// device name.
1990    DeviceNS(&'a str),
1991
1992    /// Prefix using Windows' _**U**niform **N**aming **C**onvention_, e.g.
1993    /// `\\server\share`.
1994    ///
1995    /// UNC prefixes consist of the server's hostname and a share name.
1996    UNC(&'a str, &'a str),
1997
1998    /// Prefix `C:` for the given disk drive.
1999    Disk(u8),
2000}
2001
2002impl<'a> Utf8Prefix<'a> {
2003    /// Determines if the prefix is verbatim, i.e., begins with `\\?\`.
2004    ///
2005    /// # Examples
2006    ///
2007    /// ```
2008    /// use camino::Utf8Prefix::*;
2009    ///
2010    /// assert!(Verbatim("pictures").is_verbatim());
2011    /// assert!(VerbatimUNC("server", "share").is_verbatim());
2012    /// assert!(VerbatimDisk(b'C').is_verbatim());
2013    /// assert!(!DeviceNS("BrainInterface").is_verbatim());
2014    /// assert!(!UNC("server", "share").is_verbatim());
2015    /// assert!(!Disk(b'C').is_verbatim());
2016    /// ```
2017    #[must_use]
2018    pub fn is_verbatim(&self) -> bool {
2019        use Utf8Prefix::*;
2020        match self {
2021            Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(..) => true,
2022            _ => false,
2023        }
2024    }
2025}
2026
2027/// A structure wrapping a Windows path prefix as well as its unparsed string
2028/// representation.
2029///
2030/// In addition to the parsed [`Utf8Prefix`] information returned by [`kind`],
2031/// `Utf8PrefixComponent` also holds the raw and unparsed [`str`] slice,
2032/// returned by [`as_str`].
2033///
2034/// Instances of this `struct` can be obtained by matching against the
2035/// [`Prefix` variant] on [`Utf8Component`].
2036///
2037/// Does not occur on Unix.
2038///
2039/// # Examples
2040///
2041/// ```
2042/// # if cfg!(windows) {
2043/// use camino::{Utf8Component, Utf8Path, Utf8Prefix};
2044/// use std::ffi::OsStr;
2045///
2046/// let path = Utf8Path::new(r"c:\you\later\");
2047/// match path.components().next().unwrap() {
2048///     Utf8Component::Prefix(prefix_component) => {
2049///         assert_eq!(Utf8Prefix::Disk(b'C'), prefix_component.kind());
2050///         assert_eq!("c:", prefix_component.as_str());
2051///     }
2052///     _ => unreachable!(),
2053/// }
2054/// # }
2055/// ```
2056///
2057/// [`as_str`]: Utf8PrefixComponent::as_str
2058/// [`kind`]: Utf8PrefixComponent::kind
2059/// [`Prefix` variant]: Utf8Component::Prefix
2060#[repr(transparent)]
2061#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
2062pub struct Utf8PrefixComponent<'a>(PrefixComponent<'a>);
2063
2064impl<'a> Utf8PrefixComponent<'a> {
2065    /// Returns the parsed prefix data.
2066    ///
2067    /// See [`Utf8Prefix`]'s documentation for more information on the different
2068    /// kinds of prefixes.
2069    #[must_use]
2070    pub fn kind(&self) -> Utf8Prefix<'a> {
2071        // SAFETY for all the below unsafe blocks: the path self was originally constructed from was
2072        // UTF-8 so any parts of it are valid UTF-8
2073        match self.0.kind() {
2074            Prefix::Verbatim(prefix) => Utf8Prefix::Verbatim(unsafe { str_assume_utf8(prefix) }),
2075            Prefix::VerbatimUNC(server, share) => {
2076                let server = unsafe { str_assume_utf8(server) };
2077                let share = unsafe { str_assume_utf8(share) };
2078                Utf8Prefix::VerbatimUNC(server, share)
2079            }
2080            Prefix::VerbatimDisk(drive) => Utf8Prefix::VerbatimDisk(drive),
2081            Prefix::DeviceNS(prefix) => Utf8Prefix::DeviceNS(unsafe { str_assume_utf8(prefix) }),
2082            Prefix::UNC(server, share) => {
2083                let server = unsafe { str_assume_utf8(server) };
2084                let share = unsafe { str_assume_utf8(share) };
2085                Utf8Prefix::UNC(server, share)
2086            }
2087            Prefix::Disk(drive) => Utf8Prefix::Disk(drive),
2088        }
2089    }
2090
2091    /// Returns the [`str`] slice for this prefix.
2092    #[must_use]
2093    #[inline]
2094    pub fn as_str(&self) -> &'a str {
2095        // SAFETY: Utf8PrefixComponent was constructed from a Utf8Path, so it is guaranteed to be
2096        // valid UTF-8
2097        unsafe { str_assume_utf8(self.as_os_str()) }
2098    }
2099
2100    /// Returns the raw [`OsStr`] slice for this prefix.
2101    #[must_use]
2102    #[inline]
2103    pub fn as_os_str(&self) -> &'a OsStr {
2104        self.0.as_os_str()
2105    }
2106}
2107
2108impl<'a> fmt::Debug for Utf8PrefixComponent<'a> {
2109    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2110        fmt::Debug::fmt(&self.0, f)
2111    }
2112}
2113
2114impl<'a> fmt::Display for Utf8PrefixComponent<'a> {
2115    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2116        fmt::Display::fmt(self.as_str(), f)
2117    }
2118}
2119
2120// ---
2121// read_dir_utf8
2122// ---
2123
2124/// Iterator over the entries in a directory.
2125///
2126/// This iterator is returned from [`Utf8Path::read_dir_utf8`] and will yield instances of
2127/// <code>[io::Result]<[Utf8DirEntry]></code>. Through a [`Utf8DirEntry`] information like the entry's path
2128/// and possibly other metadata can be learned.
2129///
2130/// The order in which this iterator returns entries is platform and filesystem
2131/// dependent.
2132///
2133/// # Errors
2134///
2135/// This [`io::Result`] will be an [`Err`] if there's some sort of intermittent
2136/// IO error during iteration.
2137///
2138/// If a directory entry is not UTF-8, an [`io::Error`] is returned with the
2139/// [`ErrorKind`](io::ErrorKind) set to `InvalidData` and the payload set to a [`FromPathBufError`].
2140#[derive(Debug)]
2141pub struct ReadDirUtf8 {
2142    inner: fs::ReadDir,
2143}
2144
2145impl Iterator for ReadDirUtf8 {
2146    type Item = io::Result<Utf8DirEntry>;
2147
2148    fn next(&mut self) -> Option<io::Result<Utf8DirEntry>> {
2149        self.inner
2150            .next()
2151            .map(|entry| entry.and_then(Utf8DirEntry::new))
2152    }
2153}
2154
2155/// Entries returned by the [`ReadDirUtf8`] iterator.
2156///
2157/// An instance of `Utf8DirEntry` represents an entry inside of a directory on the filesystem. Each
2158/// entry can be inspected via methods to learn about the full path or possibly other metadata.
2159#[derive(Debug)]
2160pub struct Utf8DirEntry {
2161    inner: fs::DirEntry,
2162    path: Utf8PathBuf,
2163}
2164
2165impl Utf8DirEntry {
2166    fn new(inner: fs::DirEntry) -> io::Result<Self> {
2167        let path = inner
2168            .path()
2169            .try_into()
2170            .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
2171        Ok(Self { inner, path })
2172    }
2173
2174    /// Returns the full path to the file that this entry represents.
2175    ///
2176    /// The full path is created by joining the original path to `read_dir`
2177    /// with the filename of this entry.
2178    ///
2179    /// # Examples
2180    ///
2181    /// ```no_run
2182    /// use camino::Utf8Path;
2183    ///
2184    /// fn main() -> std::io::Result<()> {
2185    ///     for entry in Utf8Path::new(".").read_dir_utf8()? {
2186    ///         let dir = entry?;
2187    ///         println!("{}", dir.path());
2188    ///     }
2189    ///     Ok(())
2190    /// }
2191    /// ```
2192    ///
2193    /// This prints output like:
2194    ///
2195    /// ```text
2196    /// ./whatever.txt
2197    /// ./foo.html
2198    /// ./hello_world.rs
2199    /// ```
2200    ///
2201    /// The exact text, of course, depends on what files you have in `.`.
2202    #[inline]
2203    pub fn path(&self) -> &Utf8Path {
2204        &self.path
2205    }
2206
2207    /// Returns the metadata for the file that this entry points at.
2208    ///
2209    /// This function will not traverse symlinks if this entry points at a symlink. To traverse
2210    /// symlinks use [`Utf8Path::metadata`] or [`fs::File::metadata`].
2211    ///
2212    /// # Platform-specific behavior
2213    ///
2214    /// On Windows this function is cheap to call (no extra system calls
2215    /// needed), but on Unix platforms this function is the equivalent of
2216    /// calling `symlink_metadata` on the path.
2217    ///
2218    /// # Examples
2219    ///
2220    /// ```
2221    /// use camino::Utf8Path;
2222    ///
2223    /// if let Ok(entries) = Utf8Path::new(".").read_dir_utf8() {
2224    ///     for entry in entries {
2225    ///         if let Ok(entry) = entry {
2226    ///             // Here, `entry` is a `Utf8DirEntry`.
2227    ///             if let Ok(metadata) = entry.metadata() {
2228    ///                 // Now let's show our entry's permissions!
2229    ///                 println!("{}: {:?}", entry.path(), metadata.permissions());
2230    ///             } else {
2231    ///                 println!("Couldn't get metadata for {}", entry.path());
2232    ///             }
2233    ///         }
2234    ///     }
2235    /// }
2236    /// ```
2237    #[inline]
2238    pub fn metadata(&self) -> io::Result<Metadata> {
2239        self.inner.metadata()
2240    }
2241
2242    /// Returns the file type for the file that this entry points at.
2243    ///
2244    /// This function will not traverse symlinks if this entry points at a
2245    /// symlink.
2246    ///
2247    /// # Platform-specific behavior
2248    ///
2249    /// On Windows and most Unix platforms this function is free (no extra
2250    /// system calls needed), but some Unix platforms may require the equivalent
2251    /// call to `symlink_metadata` to learn about the target file type.
2252    ///
2253    /// # Examples
2254    ///
2255    /// ```
2256    /// use camino::Utf8Path;
2257    ///
2258    /// if let Ok(entries) = Utf8Path::new(".").read_dir_utf8() {
2259    ///     for entry in entries {
2260    ///         if let Ok(entry) = entry {
2261    ///             // Here, `entry` is a `DirEntry`.
2262    ///             if let Ok(file_type) = entry.file_type() {
2263    ///                 // Now let's show our entry's file type!
2264    ///                 println!("{}: {:?}", entry.path(), file_type);
2265    ///             } else {
2266    ///                 println!("Couldn't get file type for {}", entry.path());
2267    ///             }
2268    ///         }
2269    ///     }
2270    /// }
2271    /// ```
2272    #[inline]
2273    pub fn file_type(&self) -> io::Result<fs::FileType> {
2274        self.inner.file_type()
2275    }
2276
2277    /// Returns the bare file name of this directory entry without any other
2278    /// leading path component.
2279    ///
2280    /// # Examples
2281    ///
2282    /// ```
2283    /// use camino::Utf8Path;
2284    ///
2285    /// if let Ok(entries) = Utf8Path::new(".").read_dir_utf8() {
2286    ///     for entry in entries {
2287    ///         if let Ok(entry) = entry {
2288    ///             // Here, `entry` is a `DirEntry`.
2289    ///             println!("{}", entry.file_name());
2290    ///         }
2291    ///     }
2292    /// }
2293    /// ```
2294    pub fn file_name(&self) -> &str {
2295        self.path
2296            .file_name()
2297            .expect("path created through DirEntry must have a filename")
2298    }
2299
2300    /// Returns the original [`fs::DirEntry`] within this [`Utf8DirEntry`].
2301    #[inline]
2302    pub fn into_inner(self) -> fs::DirEntry {
2303        self.inner
2304    }
2305
2306    /// Returns the full path to the file that this entry represents.
2307    ///
2308    /// This is analogous to [`path`], but moves ownership of the path.
2309    ///
2310    /// [`path`]: struct.Utf8DirEntry.html#method.path
2311    #[inline]
2312    #[must_use = "`self` will be dropped if the result is not used"]
2313    pub fn into_path(self) -> Utf8PathBuf {
2314        self.path
2315    }
2316}
2317
2318impl From<String> for Utf8PathBuf {
2319    fn from(string: String) -> Utf8PathBuf {
2320        Utf8PathBuf(string.into())
2321    }
2322}
2323
2324impl FromStr for Utf8PathBuf {
2325    type Err = Infallible;
2326
2327    fn from_str(s: &str) -> Result<Self, Self::Err> {
2328        Ok(Utf8PathBuf(s.into()))
2329    }
2330}
2331
2332// ---
2333// From impls: borrowed -> borrowed
2334// ---
2335
2336impl<'a> From<&'a str> for &'a Utf8Path {
2337    fn from(s: &'a str) -> &'a Utf8Path {
2338        Utf8Path::new(s)
2339    }
2340}
2341
2342// ---
2343// From impls: borrowed -> owned
2344// ---
2345
2346impl<T: ?Sized + AsRef<str>> From<&T> for Utf8PathBuf {
2347    fn from(s: &T) -> Utf8PathBuf {
2348        Utf8PathBuf::from(s.as_ref().to_owned())
2349    }
2350}
2351
2352impl<T: ?Sized + AsRef<str>> From<&T> for Box<Utf8Path> {
2353    fn from(s: &T) -> Box<Utf8Path> {
2354        Utf8PathBuf::from(s).into_boxed_path()
2355    }
2356}
2357
2358impl From<&'_ Utf8Path> for Arc<Utf8Path> {
2359    fn from(path: &Utf8Path) -> Arc<Utf8Path> {
2360        let arc: Arc<Path> = Arc::from(AsRef::<Path>::as_ref(path));
2361        let ptr = Arc::into_raw(arc) as *const Utf8Path;
2362        // SAFETY:
2363        // * path is valid UTF-8
2364        // * ptr was created by consuming an Arc<Path> so it represents an arced pointer
2365        // * Utf8Path is marked as #[repr(transparent)] so the conversion from *const Path to
2366        //   *const Utf8Path is valid
2367        unsafe { Arc::from_raw(ptr) }
2368    }
2369}
2370
2371impl From<&'_ Utf8Path> for Rc<Utf8Path> {
2372    fn from(path: &Utf8Path) -> Rc<Utf8Path> {
2373        let rc: Rc<Path> = Rc::from(AsRef::<Path>::as_ref(path));
2374        let ptr = Rc::into_raw(rc) as *const Utf8Path;
2375        // SAFETY:
2376        // * path is valid UTF-8
2377        // * ptr was created by consuming an Rc<Path> so it represents an rced pointer
2378        // * Utf8Path is marked as #[repr(transparent)] so the conversion from *const Path to
2379        //   *const Utf8Path is valid
2380        unsafe { Rc::from_raw(ptr) }
2381    }
2382}
2383
2384impl<'a> From<&'a Utf8Path> for Cow<'a, Utf8Path> {
2385    fn from(path: &'a Utf8Path) -> Cow<'a, Utf8Path> {
2386        Cow::Borrowed(path)
2387    }
2388}
2389
2390impl From<&'_ Utf8Path> for Box<Path> {
2391    fn from(path: &Utf8Path) -> Box<Path> {
2392        AsRef::<Path>::as_ref(path).into()
2393    }
2394}
2395
2396impl From<&'_ Utf8Path> for Arc<Path> {
2397    fn from(path: &Utf8Path) -> Arc<Path> {
2398        AsRef::<Path>::as_ref(path).into()
2399    }
2400}
2401
2402impl From<&'_ Utf8Path> for Rc<Path> {
2403    fn from(path: &Utf8Path) -> Rc<Path> {
2404        AsRef::<Path>::as_ref(path).into()
2405    }
2406}
2407
2408impl<'a> From<&'a Utf8Path> for Cow<'a, Path> {
2409    fn from(path: &'a Utf8Path) -> Cow<'a, Path> {
2410        Cow::Borrowed(path.as_ref())
2411    }
2412}
2413
2414// ---
2415// From impls: owned -> owned
2416// ---
2417
2418impl From<Box<Utf8Path>> for Utf8PathBuf {
2419    fn from(path: Box<Utf8Path>) -> Utf8PathBuf {
2420        path.into_path_buf()
2421    }
2422}
2423
2424impl From<Utf8PathBuf> for Box<Utf8Path> {
2425    fn from(path: Utf8PathBuf) -> Box<Utf8Path> {
2426        path.into_boxed_path()
2427    }
2428}
2429
2430impl<'a> From<Cow<'a, Utf8Path>> for Utf8PathBuf {
2431    fn from(path: Cow<'a, Utf8Path>) -> Utf8PathBuf {
2432        path.into_owned()
2433    }
2434}
2435
2436impl From<Utf8PathBuf> for String {
2437    fn from(path: Utf8PathBuf) -> String {
2438        path.into_string()
2439    }
2440}
2441
2442impl From<Utf8PathBuf> for OsString {
2443    fn from(path: Utf8PathBuf) -> OsString {
2444        path.into_os_string()
2445    }
2446}
2447
2448impl<'a> From<Utf8PathBuf> for Cow<'a, Utf8Path> {
2449    fn from(path: Utf8PathBuf) -> Cow<'a, Utf8Path> {
2450        Cow::Owned(path)
2451    }
2452}
2453
2454impl From<Utf8PathBuf> for Arc<Utf8Path> {
2455    fn from(path: Utf8PathBuf) -> Arc<Utf8Path> {
2456        let arc: Arc<Path> = Arc::from(path.0);
2457        let ptr = Arc::into_raw(arc) as *const Utf8Path;
2458        // SAFETY:
2459        // * path is valid UTF-8
2460        // * ptr was created by consuming an Arc<Path> so it represents an arced pointer
2461        // * Utf8Path is marked as #[repr(transparent)] so the conversion from *const Path to
2462        //   *const Utf8Path is valid
2463        unsafe { Arc::from_raw(ptr) }
2464    }
2465}
2466
2467impl From<Utf8PathBuf> for Rc<Utf8Path> {
2468    fn from(path: Utf8PathBuf) -> Rc<Utf8Path> {
2469        let rc: Rc<Path> = Rc::from(path.0);
2470        let ptr = Rc::into_raw(rc) as *const Utf8Path;
2471        // SAFETY:
2472        // * path is valid UTF-8
2473        // * ptr was created by consuming an Rc<Path> so it represents an rced pointer
2474        // * Utf8Path is marked as #[repr(transparent)] so the conversion from *const Path to
2475        //   *const Utf8Path is valid
2476        unsafe { Rc::from_raw(ptr) }
2477    }
2478}
2479
2480impl From<Utf8PathBuf> for PathBuf {
2481    fn from(path: Utf8PathBuf) -> PathBuf {
2482        path.0
2483    }
2484}
2485
2486impl From<Utf8PathBuf> for Box<Path> {
2487    fn from(path: Utf8PathBuf) -> Box<Path> {
2488        PathBuf::from(path).into_boxed_path()
2489    }
2490}
2491
2492impl From<Utf8PathBuf> for Arc<Path> {
2493    fn from(path: Utf8PathBuf) -> Arc<Path> {
2494        PathBuf::from(path).into()
2495    }
2496}
2497
2498impl From<Utf8PathBuf> for Rc<Path> {
2499    fn from(path: Utf8PathBuf) -> Rc<Path> {
2500        PathBuf::from(path).into()
2501    }
2502}
2503
2504impl<'a> From<Utf8PathBuf> for Cow<'a, Path> {
2505    fn from(path: Utf8PathBuf) -> Cow<'a, Path> {
2506        PathBuf::from(path).into()
2507    }
2508}
2509
2510// ---
2511// TryFrom impls
2512// ---
2513
2514impl TryFrom<PathBuf> for Utf8PathBuf {
2515    type Error = FromPathBufError;
2516
2517    fn try_from(path: PathBuf) -> Result<Utf8PathBuf, Self::Error> {
2518        Utf8PathBuf::from_path_buf(path).map_err(|path| FromPathBufError {
2519            path,
2520            error: FromPathError(()),
2521        })
2522    }
2523}
2524
2525/// Converts a [`Path`] to a [`Utf8Path`].
2526///
2527/// Returns [`FromPathError`] if the path is not valid UTF-8.
2528///
2529/// # Examples
2530///
2531/// ```
2532/// use camino::Utf8Path;
2533/// use std::convert::TryFrom;
2534/// use std::ffi::OsStr;
2535/// # #[cfg(unix)]
2536/// use std::os::unix::ffi::OsStrExt;
2537/// use std::path::Path;
2538///
2539/// let unicode_path = Path::new("/valid/unicode");
2540/// <&Utf8Path>::try_from(unicode_path).expect("valid Unicode path succeeded");
2541///
2542/// // Paths on Unix can be non-UTF-8.
2543/// # #[cfg(unix)]
2544/// let non_unicode_str = OsStr::from_bytes(b"\xFF\xFF\xFF");
2545/// # #[cfg(unix)]
2546/// let non_unicode_path = Path::new(non_unicode_str);
2547/// # #[cfg(unix)]
2548/// assert!(<&Utf8Path>::try_from(non_unicode_path).is_err(), "non-Unicode path failed");
2549/// ```
2550impl<'a> TryFrom<&'a Path> for &'a Utf8Path {
2551    type Error = FromPathError;
2552
2553    fn try_from(path: &'a Path) -> Result<&'a Utf8Path, Self::Error> {
2554        Utf8Path::from_path(path).ok_or(FromPathError(()))
2555    }
2556}
2557
2558/// A possible error value while converting a [`PathBuf`] to a [`Utf8PathBuf`].
2559///
2560/// Produced by the `TryFrom<PathBuf>` implementation for [`Utf8PathBuf`].
2561///
2562/// # Examples
2563///
2564/// ```
2565/// use camino::{Utf8PathBuf, FromPathBufError};
2566/// use std::convert::{TryFrom, TryInto};
2567/// use std::ffi::OsStr;
2568/// # #[cfg(unix)]
2569/// use std::os::unix::ffi::OsStrExt;
2570/// use std::path::PathBuf;
2571///
2572/// let unicode_path = PathBuf::from("/valid/unicode");
2573/// let utf8_path_buf: Utf8PathBuf = unicode_path.try_into().expect("valid Unicode path succeeded");
2574///
2575/// // Paths on Unix can be non-UTF-8.
2576/// # #[cfg(unix)]
2577/// let non_unicode_str = OsStr::from_bytes(b"\xFF\xFF\xFF");
2578/// # #[cfg(unix)]
2579/// let non_unicode_path = PathBuf::from(non_unicode_str);
2580/// # #[cfg(unix)]
2581/// let err: FromPathBufError = Utf8PathBuf::try_from(non_unicode_path.clone())
2582///     .expect_err("non-Unicode path failed");
2583/// # #[cfg(unix)]
2584/// assert_eq!(err.as_path(), &non_unicode_path);
2585/// # #[cfg(unix)]
2586/// assert_eq!(err.into_path_buf(), non_unicode_path);
2587/// ```
2588#[derive(Clone, Debug, Eq, PartialEq)]
2589pub struct FromPathBufError {
2590    path: PathBuf,
2591    error: FromPathError,
2592}
2593
2594impl FromPathBufError {
2595    /// Returns the [`Path`] slice that was attempted to be converted to [`Utf8PathBuf`].
2596    #[inline]
2597    pub fn as_path(&self) -> &Path {
2598        &self.path
2599    }
2600
2601    /// Returns the [`PathBuf`] that was attempted to be converted to [`Utf8PathBuf`].
2602    #[inline]
2603    pub fn into_path_buf(self) -> PathBuf {
2604        self.path
2605    }
2606
2607    /// Fetches a [`FromPathError`] for more about the conversion failure.
2608    ///
2609    /// At the moment this struct does not contain any additional information, but is provided for
2610    /// completeness.
2611    #[inline]
2612    pub fn from_path_error(&self) -> FromPathError {
2613        self.error
2614    }
2615
2616    /// Converts self into a [`std::io::Error`] with kind
2617    /// [`InvalidData`](io::ErrorKind::InvalidData).
2618    ///
2619    /// Many users of `FromPathBufError` will want to convert it into an `io::Error`. This is a
2620    /// convenience method to do that.
2621    pub fn into_io_error(self) -> io::Error {
2622        // NOTE: we don't currently implement `From<FromPathBufError> for io::Error` because we want
2623        // to ensure the user actually desires that conversion.
2624        io::Error::new(io::ErrorKind::InvalidData, self)
2625    }
2626}
2627
2628impl fmt::Display for FromPathBufError {
2629    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2630        write!(f, "PathBuf contains invalid UTF-8: {}", self.path.display())
2631    }
2632}
2633
2634impl error::Error for FromPathBufError {
2635    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
2636        Some(&self.error)
2637    }
2638}
2639
2640/// A possible error value while converting a [`Path`] to a [`Utf8Path`].
2641///
2642/// Produced by the `TryFrom<&Path>` implementation for [`&Utf8Path`](Utf8Path).
2643///
2644///
2645/// # Examples
2646///
2647/// ```
2648/// use camino::{Utf8Path, FromPathError};
2649/// use std::convert::{TryFrom, TryInto};
2650/// use std::ffi::OsStr;
2651/// # #[cfg(unix)]
2652/// use std::os::unix::ffi::OsStrExt;
2653/// use std::path::Path;
2654///
2655/// let unicode_path = Path::new("/valid/unicode");
2656/// let utf8_path: &Utf8Path = unicode_path.try_into().expect("valid Unicode path succeeded");
2657///
2658/// // Paths on Unix can be non-UTF-8.
2659/// # #[cfg(unix)]
2660/// let non_unicode_str = OsStr::from_bytes(b"\xFF\xFF\xFF");
2661/// # #[cfg(unix)]
2662/// let non_unicode_path = Path::new(non_unicode_str);
2663/// # #[cfg(unix)]
2664/// let err: FromPathError = <&Utf8Path>::try_from(non_unicode_path)
2665///     .expect_err("non-Unicode path failed");
2666/// ```
2667#[derive(Copy, Clone, Debug, Eq, PartialEq)]
2668pub struct FromPathError(());
2669
2670impl FromPathError {
2671    /// Converts self into a [`std::io::Error`] with kind
2672    /// [`InvalidData`](io::ErrorKind::InvalidData).
2673    ///
2674    /// Many users of `FromPathError` will want to convert it into an `io::Error`. This is a
2675    /// convenience method to do that.
2676    pub fn into_io_error(self) -> io::Error {
2677        // NOTE: we don't currently implement `From<FromPathBufError> for io::Error` because we want
2678        // to ensure the user actually desires that conversion.
2679        io::Error::new(io::ErrorKind::InvalidData, self)
2680    }
2681}
2682
2683impl fmt::Display for FromPathError {
2684    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2685        write!(f, "Path contains invalid UTF-8")
2686    }
2687}
2688
2689impl error::Error for FromPathError {
2690    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
2691        None
2692    }
2693}
2694
2695// ---
2696// AsRef impls
2697// ---
2698
2699impl AsRef<Utf8Path> for Utf8Path {
2700    #[inline]
2701    fn as_ref(&self) -> &Utf8Path {
2702        self
2703    }
2704}
2705
2706impl AsRef<Utf8Path> for Utf8PathBuf {
2707    #[inline]
2708    fn as_ref(&self) -> &Utf8Path {
2709        self.as_path()
2710    }
2711}
2712
2713impl AsRef<Utf8Path> for str {
2714    #[inline]
2715    fn as_ref(&self) -> &Utf8Path {
2716        Utf8Path::new(self)
2717    }
2718}
2719
2720impl AsRef<Utf8Path> for String {
2721    #[inline]
2722    fn as_ref(&self) -> &Utf8Path {
2723        Utf8Path::new(self)
2724    }
2725}
2726
2727impl AsRef<Path> for Utf8Path {
2728    #[inline]
2729    fn as_ref(&self) -> &Path {
2730        &self.0
2731    }
2732}
2733
2734impl AsRef<Path> for Utf8PathBuf {
2735    #[inline]
2736    fn as_ref(&self) -> &Path {
2737        &self.0
2738    }
2739}
2740
2741impl AsRef<str> for Utf8Path {
2742    #[inline]
2743    fn as_ref(&self) -> &str {
2744        self.as_str()
2745    }
2746}
2747
2748impl AsRef<str> for Utf8PathBuf {
2749    #[inline]
2750    fn as_ref(&self) -> &str {
2751        self.as_str()
2752    }
2753}
2754
2755impl AsRef<OsStr> for Utf8Path {
2756    #[inline]
2757    fn as_ref(&self) -> &OsStr {
2758        self.as_os_str()
2759    }
2760}
2761
2762impl AsRef<OsStr> for Utf8PathBuf {
2763    #[inline]
2764    fn as_ref(&self) -> &OsStr {
2765        self.as_os_str()
2766    }
2767}
2768
2769// ---
2770// Borrow and ToOwned
2771// ---
2772
2773impl Borrow<Utf8Path> for Utf8PathBuf {
2774    #[inline]
2775    fn borrow(&self) -> &Utf8Path {
2776        self.as_path()
2777    }
2778}
2779
2780impl ToOwned for Utf8Path {
2781    type Owned = Utf8PathBuf;
2782
2783    #[inline]
2784    fn to_owned(&self) -> Utf8PathBuf {
2785        self.to_path_buf()
2786    }
2787}
2788
2789impl<P: AsRef<Utf8Path>> std::iter::FromIterator<P> for Utf8PathBuf {
2790    fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Utf8PathBuf {
2791        let mut buf = Utf8PathBuf::new();
2792        buf.extend(iter);
2793        buf
2794    }
2795}
2796
2797// ---
2798// [Partial]Eq, [Partial]Ord, Hash
2799// ---
2800
2801impl PartialEq for Utf8PathBuf {
2802    #[inline]
2803    fn eq(&self, other: &Utf8PathBuf) -> bool {
2804        self.components() == other.components()
2805    }
2806}
2807
2808impl Eq for Utf8PathBuf {}
2809
2810impl Hash for Utf8PathBuf {
2811    #[inline]
2812    fn hash<H: Hasher>(&self, state: &mut H) {
2813        self.as_path().hash(state)
2814    }
2815}
2816
2817impl PartialOrd for Utf8PathBuf {
2818    #[inline]
2819    fn partial_cmp(&self, other: &Utf8PathBuf) -> Option<Ordering> {
2820        Some(self.cmp(other))
2821    }
2822}
2823
2824impl Ord for Utf8PathBuf {
2825    fn cmp(&self, other: &Utf8PathBuf) -> Ordering {
2826        self.components().cmp(other.components())
2827    }
2828}
2829
2830impl PartialEq for Utf8Path {
2831    #[inline]
2832    fn eq(&self, other: &Utf8Path) -> bool {
2833        self.components().eq(other.components())
2834    }
2835}
2836
2837impl Eq for Utf8Path {}
2838
2839impl Hash for Utf8Path {
2840    fn hash<H: Hasher>(&self, state: &mut H) {
2841        for component in self.components() {
2842            component.hash(state)
2843        }
2844    }
2845}
2846
2847impl PartialOrd for Utf8Path {
2848    #[inline]
2849    fn partial_cmp(&self, other: &Utf8Path) -> Option<Ordering> {
2850        Some(self.cmp(other))
2851    }
2852}
2853
2854impl Ord for Utf8Path {
2855    fn cmp(&self, other: &Utf8Path) -> Ordering {
2856        self.components().cmp(other.components())
2857    }
2858}
2859
2860impl<'a> IntoIterator for &'a Utf8PathBuf {
2861    type Item = &'a str;
2862    type IntoIter = Iter<'a>;
2863    #[inline]
2864    fn into_iter(self) -> Iter<'a> {
2865        self.iter()
2866    }
2867}
2868
2869impl<'a> IntoIterator for &'a Utf8Path {
2870    type Item = &'a str;
2871    type IntoIter = Iter<'a>;
2872    #[inline]
2873    fn into_iter(self) -> Iter<'a> {
2874        self.iter()
2875    }
2876}
2877
2878macro_rules! impl_cmp {
2879    ($lhs:ty, $rhs: ty) => {
2880        #[allow(clippy::extra_unused_lifetimes)]
2881        impl<'a, 'b> PartialEq<$rhs> for $lhs {
2882            #[inline]
2883            fn eq(&self, other: &$rhs) -> bool {
2884                <Utf8Path as PartialEq>::eq(self, other)
2885            }
2886        }
2887
2888        #[allow(clippy::extra_unused_lifetimes)]
2889        impl<'a, 'b> PartialEq<$lhs> for $rhs {
2890            #[inline]
2891            fn eq(&self, other: &$lhs) -> bool {
2892                <Utf8Path as PartialEq>::eq(self, other)
2893            }
2894        }
2895
2896        #[allow(clippy::extra_unused_lifetimes)]
2897        impl<'a, 'b> PartialOrd<$rhs> for $lhs {
2898            #[inline]
2899            fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
2900                <Utf8Path as PartialOrd>::partial_cmp(self, other)
2901            }
2902        }
2903
2904        #[allow(clippy::extra_unused_lifetimes)]
2905        impl<'a, 'b> PartialOrd<$lhs> for $rhs {
2906            #[inline]
2907            fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
2908                <Utf8Path as PartialOrd>::partial_cmp(self, other)
2909            }
2910        }
2911    };
2912}
2913
2914impl_cmp!(Utf8PathBuf, Utf8Path);
2915impl_cmp!(Utf8PathBuf, &'a Utf8Path);
2916impl_cmp!(Cow<'a, Utf8Path>, Utf8Path);
2917impl_cmp!(Cow<'a, Utf8Path>, &'b Utf8Path);
2918impl_cmp!(Cow<'a, Utf8Path>, Utf8PathBuf);
2919
2920macro_rules! impl_cmp_std_path {
2921    ($lhs:ty, $rhs: ty) => {
2922        #[allow(clippy::extra_unused_lifetimes)]
2923        impl<'a, 'b> PartialEq<$rhs> for $lhs {
2924            #[inline]
2925            fn eq(&self, other: &$rhs) -> bool {
2926                <Path as PartialEq>::eq(self.as_ref(), other)
2927            }
2928        }
2929
2930        #[allow(clippy::extra_unused_lifetimes)]
2931        impl<'a, 'b> PartialEq<$lhs> for $rhs {
2932            #[inline]
2933            fn eq(&self, other: &$lhs) -> bool {
2934                <Path as PartialEq>::eq(self, other.as_ref())
2935            }
2936        }
2937
2938        #[allow(clippy::extra_unused_lifetimes)]
2939        impl<'a, 'b> PartialOrd<$rhs> for $lhs {
2940            #[inline]
2941            fn partial_cmp(&self, other: &$rhs) -> Option<std::cmp::Ordering> {
2942                <Path as PartialOrd>::partial_cmp(self.as_ref(), other)
2943            }
2944        }
2945
2946        #[allow(clippy::extra_unused_lifetimes)]
2947        impl<'a, 'b> PartialOrd<$lhs> for $rhs {
2948            #[inline]
2949            fn partial_cmp(&self, other: &$lhs) -> Option<std::cmp::Ordering> {
2950                <Path as PartialOrd>::partial_cmp(self, other.as_ref())
2951            }
2952        }
2953    };
2954}
2955
2956impl_cmp_std_path!(Utf8PathBuf, Path);
2957impl_cmp_std_path!(Utf8PathBuf, &'a Path);
2958impl_cmp_std_path!(Utf8PathBuf, Cow<'a, Path>);
2959impl_cmp_std_path!(Utf8PathBuf, PathBuf);
2960impl_cmp_std_path!(Utf8Path, Path);
2961impl_cmp_std_path!(Utf8Path, &'a Path);
2962impl_cmp_std_path!(Utf8Path, Cow<'a, Path>);
2963impl_cmp_std_path!(Utf8Path, PathBuf);
2964impl_cmp_std_path!(&'a Utf8Path, Path);
2965impl_cmp_std_path!(&'a Utf8Path, Cow<'b, Path>);
2966impl_cmp_std_path!(&'a Utf8Path, PathBuf);
2967// NOTE: impls for Cow<'a, Utf8Path> cannot be defined because of the orphan rule (E0117)
2968
2969macro_rules! impl_cmp_str {
2970    ($lhs:ty, $rhs: ty) => {
2971        #[allow(clippy::extra_unused_lifetimes)]
2972        impl<'a, 'b> PartialEq<$rhs> for $lhs {
2973            #[inline]
2974            fn eq(&self, other: &$rhs) -> bool {
2975                <Utf8Path as PartialEq>::eq(self, Utf8Path::new(other))
2976            }
2977        }
2978
2979        #[allow(clippy::extra_unused_lifetimes)]
2980        impl<'a, 'b> PartialEq<$lhs> for $rhs {
2981            #[inline]
2982            fn eq(&self, other: &$lhs) -> bool {
2983                <Utf8Path as PartialEq>::eq(Utf8Path::new(self), other)
2984            }
2985        }
2986
2987        #[allow(clippy::extra_unused_lifetimes)]
2988        impl<'a, 'b> PartialOrd<$rhs> for $lhs {
2989            #[inline]
2990            fn partial_cmp(&self, other: &$rhs) -> Option<std::cmp::Ordering> {
2991                <Utf8Path as PartialOrd>::partial_cmp(self, Utf8Path::new(other))
2992            }
2993        }
2994
2995        #[allow(clippy::extra_unused_lifetimes)]
2996        impl<'a, 'b> PartialOrd<$lhs> for $rhs {
2997            #[inline]
2998            fn partial_cmp(&self, other: &$lhs) -> Option<std::cmp::Ordering> {
2999                <Utf8Path as PartialOrd>::partial_cmp(Utf8Path::new(self), other)
3000            }
3001        }
3002    };
3003}
3004
3005impl_cmp_str!(Utf8PathBuf, str);
3006impl_cmp_str!(Utf8PathBuf, &'a str);
3007impl_cmp_str!(Utf8PathBuf, Cow<'a, str>);
3008impl_cmp_str!(Utf8PathBuf, String);
3009impl_cmp_str!(Utf8Path, str);
3010impl_cmp_str!(Utf8Path, &'a str);
3011impl_cmp_str!(Utf8Path, Cow<'a, str>);
3012impl_cmp_str!(Utf8Path, String);
3013impl_cmp_str!(&'a Utf8Path, str);
3014impl_cmp_str!(&'a Utf8Path, Cow<'b, str>);
3015impl_cmp_str!(&'a Utf8Path, String);
3016// NOTE: impls for Cow<'a, Utf8Path> cannot be defined because of the orphan rule (E0117)
3017
3018macro_rules! impl_cmp_os_str {
3019    ($lhs:ty, $rhs: ty) => {
3020        #[allow(clippy::extra_unused_lifetimes)]
3021        impl<'a, 'b> PartialEq<$rhs> for $lhs {
3022            #[inline]
3023            fn eq(&self, other: &$rhs) -> bool {
3024                <Path as PartialEq>::eq(self.as_ref(), other.as_ref())
3025            }
3026        }
3027
3028        #[allow(clippy::extra_unused_lifetimes)]
3029        impl<'a, 'b> PartialEq<$lhs> for $rhs {
3030            #[inline]
3031            fn eq(&self, other: &$lhs) -> bool {
3032                <Path as PartialEq>::eq(self.as_ref(), other.as_ref())
3033            }
3034        }
3035
3036        #[allow(clippy::extra_unused_lifetimes)]
3037        impl<'a, 'b> PartialOrd<$rhs> for $lhs {
3038            #[inline]
3039            fn partial_cmp(&self, other: &$rhs) -> Option<std::cmp::Ordering> {
3040                <Path as PartialOrd>::partial_cmp(self.as_ref(), other.as_ref())
3041            }
3042        }
3043
3044        #[allow(clippy::extra_unused_lifetimes)]
3045        impl<'a, 'b> PartialOrd<$lhs> for $rhs {
3046            #[inline]
3047            fn partial_cmp(&self, other: &$lhs) -> Option<std::cmp::Ordering> {
3048                <Path as PartialOrd>::partial_cmp(self.as_ref(), other.as_ref())
3049            }
3050        }
3051    };
3052}
3053
3054impl_cmp_os_str!(Utf8PathBuf, OsStr);
3055impl_cmp_os_str!(Utf8PathBuf, &'a OsStr);
3056impl_cmp_os_str!(Utf8PathBuf, Cow<'a, OsStr>);
3057impl_cmp_os_str!(Utf8PathBuf, OsString);
3058impl_cmp_os_str!(Utf8Path, OsStr);
3059impl_cmp_os_str!(Utf8Path, &'a OsStr);
3060impl_cmp_os_str!(Utf8Path, Cow<'a, OsStr>);
3061impl_cmp_os_str!(Utf8Path, OsString);
3062impl_cmp_os_str!(&'a Utf8Path, OsStr);
3063impl_cmp_os_str!(&'a Utf8Path, Cow<'b, OsStr>);
3064impl_cmp_os_str!(&'a Utf8Path, OsString);
3065// NOTE: impls for Cow<'a, Utf8Path> cannot be defined because of the orphan rule (E0117)
3066
3067/// Makes the path absolute without accessing the filesystem, converting it to a [`Utf8PathBuf`].
3068///
3069/// If the path is relative, the current directory is used as the base directory. All intermediate
3070/// components will be resolved according to platform-specific rules, but unlike
3071/// [`canonicalize`][Utf8Path::canonicalize] or [`canonicalize_utf8`](Utf8Path::canonicalize_utf8),
3072/// this does not resolve symlinks and may succeed even if the path does not exist.
3073///
3074/// *Requires Rust 1.79 or newer.*
3075///
3076/// # Errors
3077///
3078/// Errors if:
3079///
3080/// * The path is empty.
3081/// * The [current directory][std::env::current_dir] cannot be determined.
3082/// * The path is not valid UTF-8.
3083///
3084/// # Examples
3085///
3086/// ## POSIX paths
3087///
3088/// ```
3089/// # #[cfg(unix)]
3090/// fn main() -> std::io::Result<()> {
3091///     use camino::Utf8Path;
3092///
3093///     // Relative to absolute
3094///     let absolute = camino::absolute_utf8("foo/./bar")?;
3095///     assert!(absolute.ends_with("foo/bar"));
3096///
3097///     // Absolute to absolute
3098///     let absolute = camino::absolute_utf8("/foo//test/.././bar.rs")?;
3099///     assert_eq!(absolute, Utf8Path::new("/foo/test/../bar.rs"));
3100///     Ok(())
3101/// }
3102/// # #[cfg(not(unix))]
3103/// # fn main() {}
3104/// ```
3105///
3106/// The path is resolved using [POSIX semantics][posix-semantics] except that it stops short of
3107/// resolving symlinks. This means it will keep `..` components and trailing slashes.
3108///
3109/// ## Windows paths
3110///
3111/// ```
3112/// # #[cfg(windows)]
3113/// fn main() -> std::io::Result<()> {
3114///     use camino::Utf8Path;
3115///
3116///     // Relative to absolute
3117///     let absolute = camino::absolute_utf8("foo/./bar")?;
3118///     assert!(absolute.ends_with(r"foo\bar"));
3119///
3120///     // Absolute to absolute
3121///     let absolute = camino::absolute_utf8(r"C:\foo//test\..\./bar.rs")?;
3122///
3123///     assert_eq!(absolute, Utf8Path::new(r"C:\foo\bar.rs"));
3124///     Ok(())
3125/// }
3126/// # #[cfg(not(windows))]
3127/// # fn main() {}
3128/// ```
3129///
3130/// For verbatim paths this will simply return the path as given. For other paths this is currently
3131/// equivalent to calling [`GetFullPathNameW`][windows-path].
3132///
3133/// Note that this [may change in the future][changes].
3134///
3135/// [changes]: io#platform-specific-behavior
3136/// [posix-semantics]:
3137///     https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
3138/// [windows-path]:
3139///     https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew
3140#[cfg(absolute_path)]
3141pub fn absolute_utf8<P: AsRef<Path>>(path: P) -> io::Result<Utf8PathBuf> {
3142    // Note that even if the passed in path is valid UTF-8, it is not guaranteed that the absolute
3143    // path will be valid UTF-8. For example, the current directory may not be valid UTF-8.
3144    //
3145    // That's why we take `AsRef<Path>` instead of `AsRef<Utf8Path>` here -- we have to pay the cost
3146    // of checking for valid UTF-8 anyway.
3147    let path = path.as_ref();
3148    #[allow(clippy::incompatible_msrv)]
3149    Utf8PathBuf::try_from(std::path::absolute(path)?).map_err(|error| error.into_io_error())
3150}
3151
3152// invariant: OsStr must be guaranteed to be utf8 data
3153#[inline]
3154unsafe fn str_assume_utf8(string: &OsStr) -> &str {
3155    #[cfg(os_str_bytes)]
3156    {
3157        // SAFETY: OsStr is guaranteed to be utf8 data from the invariant
3158        unsafe {
3159            std::str::from_utf8_unchecked(
3160                #[allow(clippy::incompatible_msrv)]
3161                string.as_encoded_bytes(),
3162            )
3163        }
3164    }
3165    #[cfg(not(os_str_bytes))]
3166    {
3167        // Adapted from the source code for Option::unwrap_unchecked.
3168        match string.to_str() {
3169            Some(val) => val,
3170            None => std::hint::unreachable_unchecked(),
3171        }
3172    }
3173}