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