diff --git a/src/path/components.rs b/src/path/components.rs new file mode 100644 index 0000000..51649c5 --- /dev/null +++ b/src/path/components.rs @@ -0,0 +1,82 @@ +use std::ffi::OsStr; +use std::iter::FusedIterator; + +use crate::path::{Component, Path}; + +/// An iterator over the [`Component`]s of a [`Path`]. +/// +/// This `struct` is created by the [`components`] method on [`Path`]. +/// See its documentation for more. +/// +/// # Examples +/// +/// ``` +/// use async_std::path::Path; +/// +/// let path = Path::new("/tmp/foo/bar.txt"); +/// +/// for component in path.components() { +/// println!("{:?}", component); +/// } +/// ``` +/// +/// [`Component`]: enum.Component.html +/// [`components`]: struct.Path.html#method.components +/// [`Path`]: struct.Path.html +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Components<'a> { + pub(crate) inner: std::path::Components<'a>, +} + +impl<'a> Components<'a> { + /// Extracts a slice corresponding to the portion of the path remaining for iteration. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let mut components = Path::new("/tmp/foo/bar.txt").components(); + /// components.next(); + /// components.next(); + /// + /// assert_eq!(Path::new("foo/bar.txt"), components.as_path()); + /// ``` + pub fn as_path(&self) -> &'a Path { + self.inner.as_path().into() + } +} + +impl AsRef for Components<'_> { + fn as_ref(&self) -> &Path { + self.as_path() + } +} + +impl AsRef for Components<'_> { + fn as_ref(&self) -> &OsStr { + self.as_path().as_os_str() + } +} + +impl<'a> Iterator for Components<'a> { + type Item = Component<'a>; + + fn next(&mut self) -> Option> { + self.inner.next() + } +} + +impl<'a> DoubleEndedIterator for Components<'a> { + fn next_back(&mut self) -> Option> { + self.inner.next_back() + } +} + +impl FusedIterator for Components<'_> {} + +impl AsRef for Component<'_> { + fn as_ref(&self) -> &Path { + self.as_os_str().as_ref() + } +} diff --git a/src/path/iter.rs b/src/path/iter.rs new file mode 100644 index 0000000..b406100 --- /dev/null +++ b/src/path/iter.rs @@ -0,0 +1,82 @@ +use std::ffi::OsStr; +use std::fmt; +use std::iter::FusedIterator; + +use crate::path::{Component, Components, Path}; + +/// An iterator over the [`Component`]s of a [`Path`], as [`OsStr`] slices. +/// +/// This `struct` is created by the [`iter`] method on [`Path`]. +/// See its documentation for more. +/// +/// [`Component`]: enum.Component.html +/// [`iter`]: struct.Path.html#method.iter +/// [`OsStr`]: ../../std/ffi/struct.OsStr.html +/// [`Path`]: struct.Path.html +#[derive(Clone)] +pub struct Iter<'a> { + pub(crate) inner: Components<'a>, +} + +impl<'a> Iter<'a> { + /// Extracts a slice corresponding to the portion of the path remaining for iteration. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let mut iter = Path::new("/tmp/foo/bar.txt").iter(); + /// iter.next(); + /// iter.next(); + /// + /// assert_eq!(Path::new("foo/bar.txt"), iter.as_path()); + /// ``` + pub fn as_path(&self) -> &'a Path { + self.inner.as_path() + } +} + +impl<'a> Iterator for Iter<'a> { + type Item = &'a OsStr; + + fn next(&mut self) -> Option<&'a OsStr> { + self.inner.next().map(Component::as_os_str) + } +} + +impl fmt::Debug for Iter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct DebugHelper<'a>(&'a Path); + + impl fmt::Debug for DebugHelper<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.0.iter()).finish() + } + } + + f.debug_tuple("Iter") + .field(&DebugHelper(self.as_path())) + .finish() + } +} + +impl AsRef for Iter<'_> { + fn as_ref(&self) -> &Path { + self.as_path() + } +} + +impl AsRef for Iter<'_> { + fn as_ref(&self) -> &OsStr { + self.as_path().as_os_str() + } +} + +impl<'a> DoubleEndedIterator for Iter<'a> { + fn next_back(&mut self) -> Option<&'a OsStr> { + self.inner.next_back().map(Component::as_os_str) + } +} + +impl FusedIterator for Iter<'_> {} diff --git a/src/path/mod.rs b/src/path/mod.rs index 059e605..7ce9b62 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -70,25 +70,18 @@ //! [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html mod ancestors; +mod components; +mod iter; mod path; mod pathbuf; -// Structs re-export #[doc(inline)] -pub use std::path::{Components, Display, Iter, PrefixComponent, StripPrefixError}; +pub use std::path::{ + is_separator, Component, Display, Prefix, PrefixComponent, StripPrefixError, MAIN_SEPARATOR, +}; -// Enums re-export -#[doc(inline)] -pub use std::path::{Component, Prefix}; - -// Constants re-export -#[doc(inline)] -pub use std::path::MAIN_SEPARATOR; - -// Functions re-export -#[doc(inline)] -pub use std::path::is_separator; - -use ancestors::Ancestors; +pub use ancestors::Ancestors; +pub use components::Components; +pub use iter::Iter; pub use path::Path; pub use pathbuf::PathBuf; diff --git a/src/path/path.rs b/src/path/path.rs index 43adbbb..dfe9426 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -1,12 +1,51 @@ -use std::ffi::OsStr; +use std::borrow::{Cow, ToOwned}; +use std::cmp::Ordering; +use std::ffi::{OsStr, OsString}; +use std::rc::Rc; +use std::sync::Arc; +use crate::fs; +use crate::io; use crate::path::{Ancestors, Components, Display, Iter, PathBuf, StripPrefixError}; -use crate::{fs, io}; +/// A slice of a path. +/// /// This struct is an async version of [`std::path::Path`]. /// +/// This type supports a number of operations for inspecting a path, including +/// breaking the path into its components (separated by `/` on Unix and by either +/// `/` or `\` on Windows), extracting the file name, determining whether the path +/// is absolute, and so on. +/// +/// This is an *unsized* type, meaning that it must always be used behind a +/// pointer like `&` or `Box`. For an owned version of this type, +/// see [`PathBuf`]. +/// +/// [`PathBuf`]: struct.PathBuf.html /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.Path.html -#[derive(Debug, PartialEq)] +/// +/// More details about the overall approach can be found in +/// the [module documentation](index.html). +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// use std::ffi::OsStr; +/// +/// // Note: this example does work on Windows +/// let path = Path::new("./foo/bar.txt"); +/// +/// let parent = path.parent(); +/// assert_eq!(parent, Some(Path::new("./foo"))); +/// +/// let file_stem = path.file_stem(); +/// assert_eq!(file_stem, Some(OsStr::new("bar"))); +/// +/// let extension = path.extension(); +/// assert_eq!(extension, Some(OsStr::new("txt"))); +/// ``` +#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Path { inner: std::path::Path, } @@ -38,14 +77,25 @@ impl Path { unsafe { &*(std::path::Path::new(s) as *const std::path::Path as *const Path) } } - /// Yields the underlying [`OsStr`] slice. + /// Returns the underlying [`OsStr`] slice. /// /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// use async_std::path::Path; + /// + /// let os_str = Path::new("foo.txt").as_os_str(); + /// assert_eq!(os_str, OsStr::new("foo.txt")); + /// ``` pub fn as_os_str(&self) -> &OsStr { self.inner.as_os_str() } - /// Yields a [`&str`] slice if the `Path` is valid unicode. + /// Returns a [`&str`] slice if the `Path` is valid unicode. /// /// This conversion may entail doing a check for UTF-8 validity. /// Note that validation is performed because non-UTF-8 strings are @@ -86,7 +136,7 @@ impl Path { /// /// Had `path` contained invalid unicode, the `to_string_lossy` call might /// have returned `"fo�.txt"`. - pub fn to_string_lossy(&self) -> std::borrow::Cow<'_, str> { + pub fn to_string_lossy(&self) -> Cow<'_, str> { self.inner.to_string_lossy() } @@ -106,14 +156,16 @@ impl Path { PathBuf::from(self.inner.to_path_buf()) } - /// Returns `true` if the `Path` is absolute, i.e., if it is independent of + /// Returns `true` if the `Path` is absolute, i.e. if it is independent of /// the current directory. /// /// * On Unix, a path is absolute if it starts with the root, so - /// `is_absolute` and [`has_root`] are equivalent. + /// `is_absolute` and [`has_root`] are equivalent. /// /// * On Windows, a path is absolute if it has a prefix and starts with the - /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. + /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. + /// + /// [`has_root`]: #method.has_root /// /// # Examples /// @@ -122,16 +174,16 @@ impl Path { /// /// assert!(!Path::new("foo.txt").is_absolute()); /// ``` - /// - /// [`has_root`]: #method.has_root pub fn is_absolute(&self) -> bool { self.inner.is_absolute() } - /// Returns `true` if the `Path` is relative, i.e., not absolute. + /// Returns `true` if the `Path` is relative, i.e. not absolute. /// /// See [`is_absolute`]'s documentation for more details. /// + /// [`is_absolute`]: #method.is_absolute + /// /// # Examples /// /// ``` @@ -139,8 +191,6 @@ impl Path { /// /// assert!(Path::new("foo.txt").is_relative()); /// ``` - /// - /// [`is_absolute`]: #method.is_absolute pub fn is_relative(&self) -> bool { self.inner.is_relative() } @@ -150,9 +200,9 @@ impl Path { /// * On Unix, a path has a root if it begins with `/`. /// /// * On Windows, a path has a root if it: - /// * has no prefix and begins with a separator, e.g., `\windows` - /// * has a prefix followed by a separator, e.g., `c:\windows` but not `c:windows` - /// * has any non-disk prefix, e.g., `\\server\share` + /// * has no prefix and begins with a separator, e.g. `\windows` + /// * has a prefix followed by a separator, e.g. `c:\windows` but not `c:windows` + /// * has any non-disk prefix, e.g. `\\server\share` /// /// # Examples /// @@ -196,6 +246,9 @@ impl Path { /// [`None`], the iterator will do likewise. The iterator will always yield at least one value, /// namely `&self`. /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html + /// [`parent`]: struct.Path.html#method.parent + /// /// # Examples /// /// ``` @@ -207,9 +260,6 @@ impl Path { /// assert_eq!(ancestors.next(), Some(Path::new("/").into())); /// assert_eq!(ancestors.next(), None); /// ``` - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html - /// [`parent`]: struct.Path.html#method.parent pub fn ancestors(&self) -> Ancestors<'_> { Ancestors { next: Some(&self) } } @@ -226,9 +276,10 @@ impl Path { /// # Examples /// /// ``` - /// use async_std::path::Path; /// use std::ffi::OsStr; /// + /// use async_std::path::Path; + /// /// assert_eq!(Some(OsStr::new("bin")), Path::new("/usr/bin/").file_name()); /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("tmp/foo.txt").file_name()); /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.").file_name()); @@ -240,7 +291,7 @@ impl Path { self.inner.file_name() } - /// Returns a path that, when joined onto `base`, yields `self`. + /// Returns a path that becomes `self` when joined onto `base`. /// /// # Errors /// @@ -314,15 +365,15 @@ impl Path { self.inner.ends_with(child.as_ref()) } - /// Extracts the stem (non-extension) portion of [`self.file_name`]. + /// Extracts the stem (non-extension) portion of [`file_name`]. /// - /// [`self.file_name`]: struct.Path.html#method.file_name + /// [`file_name`]: struct.Path.html#method.file_name /// /// The stem is: /// - /// * [`None`], if there is no file name; - /// * The entire file name if there is no embedded `.`; - /// * The entire file name if the file name begins with `.` and has no other `.`s within; + /// * [`None`], if there is no file name + /// * The entire file name if there is no embedded `.` + /// * The entire file name if the file name begins with `.` and has no other `.`s within /// * Otherwise, the portion of the file name before the final `.` /// /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None @@ -340,16 +391,16 @@ impl Path { self.inner.file_stem() } - /// Extracts the extension of [`self.file_name`], if possible. + /// Extracts the extension of [`file_name`], if possible. /// /// The extension is: /// - /// * [`None`], if there is no file name; - /// * [`None`], if there is no embedded `.`; - /// * [`None`], if the file name begins with `.` and has no other `.`s within; + /// * [`None`], if there is no file name + /// * [`None`], if there is no embedded `.` + /// * [`None`], if the file name begins with `.` and has no other `.`s within /// * Otherwise, the portion of the file name after the final `.` /// - /// [`self.file_name`]: struct.Path.html#method.file_name + /// [`file_name`]: struct.Path.html#method.file_name /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None /// /// # Examples @@ -442,24 +493,27 @@ impl Path { /// and `a/b/../c` are distinct, to account for the possibility that `b` /// is a symbolic link (so its parent isn't `a`). /// + /// [`Component`]: enum.Component.html + /// [`CurDir`]: enum.Component.html#variant.CurDir + /// /// # Examples /// /// ``` - /// use async_std::path::{Path, Component}; /// use std::ffi::OsStr; /// + /// use async_std::path::{Path, Component}; + /// /// let mut components = Path::new("/tmp/foo.txt").components(); /// /// assert_eq!(components.next(), Some(Component::RootDir)); /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("tmp")))); /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("foo.txt")))); - /// assert_eq!(components.next(), None) + /// assert_eq!(components.next(), None); /// ``` - /// - /// [`Component`]: enum.Component.html - /// [`CurDir`]: enum.Component.html#variant.CurDir pub fn components(&self) -> Components<'_> { - self.inner.components() + Components { + inner: self.inner.components(), + } } /// Produces an iterator over the path's components viewed as [`OsStr`] @@ -474,9 +528,10 @@ impl Path { /// # Examples /// /// ``` - /// use async_std::path::{self, Path}; /// use std::ffi::OsStr; /// + /// use async_std::path::{self, Path}; + /// /// let mut it = Path::new("/tmp/foo.txt").iter(); /// assert_eq!(it.next(), Some(OsStr::new(&path::MAIN_SEPARATOR.to_string()))); /// assert_eq!(it.next(), Some(OsStr::new("tmp"))); @@ -484,7 +539,9 @@ impl Path { /// assert_eq!(it.next(), None) /// ``` pub fn iter(&self) -> Iter<'_> { - self.inner.iter() + Iter { + inner: self.components(), + } } /// Returns an object that implements [`Display`] for safely printing paths @@ -505,7 +562,7 @@ impl Path { self.inner.display() } - /// Queries the file system to get information about a file, directory, etc. + /// Reads the metadata of a file or directory. /// /// This function will traverse symbolic links to query information about the /// destination file. @@ -522,7 +579,7 @@ impl Path { /// use async_std::path::Path; /// /// let path = Path::new("/Minas/tirith"); - /// let metadata = path.metadata().await.expect("metadata call failed"); + /// let metadata = path.metadata().await?; /// println!("{:?}", metadata.file_type()); /// # /// # Ok(()) }) } @@ -531,7 +588,7 @@ impl Path { fs::metadata(self).await } - /// Queries the metadata about a file without following symlinks. + /// Reads the metadata of a file or directory without following symbolic links. /// /// This is an alias to [`fs::symlink_metadata`]. /// @@ -545,7 +602,7 @@ impl Path { /// use async_std::path::Path; /// /// let path = Path::new("/Minas/tirith"); - /// let metadata = path.symlink_metadata().await.expect("symlink_metadata call failed"); + /// let metadata = path.symlink_metadata().await?; /// println!("{:?}", metadata.file_type()); /// # /// # Ok(()) }) } @@ -554,8 +611,10 @@ impl Path { fs::symlink_metadata(self).await } - /// Returns the canonical, absolute form of the path with all intermediate - /// components normalized and symbolic links resolved. + /// Returns the canonical form of a path. + /// + /// The returned path is in absolute form with all intermediate components normalized and + /// symbolic links resolved. /// /// This is an alias to [`fs::canonicalize`]. /// @@ -569,7 +628,7 @@ impl Path { /// use async_std::path::{Path, PathBuf}; /// /// let path = Path::new("/foo/test/../test/bar.rs"); - /// assert_eq!(path.canonicalize().await.unwrap(), PathBuf::from("/foo/test/bar.rs")); + /// assert_eq!(path.canonicalize().await?, PathBuf::from("/foo/test/bar.rs")); /// # /// # Ok(()) }) } /// ``` @@ -591,7 +650,7 @@ impl Path { /// use async_std::path::Path; /// /// let path = Path::new("/laputa/sky_castle.rs"); - /// let path_link = path.read_link().await.expect("read_link call failed"); + /// let path_link = path.read_link().await?; /// # /// # Ok(()) }) } /// ``` @@ -599,9 +658,9 @@ impl Path { fs::read_link(self).await } - /// Returns an iterator over the entries within a directory. + /// Returns a stream over the entries within a directory. /// - /// The iterator will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. New + /// The stream will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. New /// errors may be encountered after an iterator is initially constructed. /// /// This is an alias to [`fs::read_dir`]. @@ -620,7 +679,8 @@ impl Path { /// use async_std::prelude::*; /// /// let path = Path::new("/laputa"); - /// let mut dir = fs::read_dir(&path).await.expect("read_dir call failed"); + /// let mut dir = fs::read_dir(&path).await?; + /// /// while let Some(res) = dir.next().await { /// let entry = res?; /// println!("{}", entry.file_name().to_string_lossy()); @@ -710,6 +770,7 @@ impl Path { /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::path::Path; + /// /// assert_eq!(Path::new("./is_a_directory/").is_dir().await, true); /// assert_eq!(Path::new("a_file.txt").is_dir().await, false); /// # @@ -736,6 +797,15 @@ impl Path { /// /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html /// [`PathBuf`]: struct.PathBuf.html + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path: Box = Path::new("foo.txt").into(); + /// let path_buf = path.into_path_buf(); + /// ``` pub fn into_path_buf(self: Box) -> PathBuf { let rw = Box::into_raw(self) as *mut std::path::Path; let inner = unsafe { Box::from_raw(rw) }; @@ -743,27 +813,42 @@ impl Path { } } -impl<'a> From<&'a std::path::Path> for &'a Path { - fn from(path: &'a std::path::Path) -> &'a Path { - &Path::new(path.as_os_str()) +impl From<&Path> for Box { + fn from(path: &Path) -> Box { + let boxed: Box = path.inner.into(); + let rw = Box::into_raw(boxed) as *mut Path; + unsafe { Box::from_raw(rw) } } } -impl<'a> Into<&'a std::path::Path> for &'a Path { - fn into(self) -> &'a std::path::Path { - std::path::Path::new(&self.inner) +impl From<&Path> for Arc { + /// Converts a Path into a Rc by copying the Path data into a new Rc buffer. + #[inline] + fn from(s: &Path) -> Arc { + let arc: Arc = Arc::from(s.as_os_str()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } } } -impl AsRef for Path { - fn as_ref(&self) -> &std::path::Path { - self.into() +impl From<&Path> for Rc { + #[inline] + fn from(s: &Path) -> Rc { + let rc: Rc = Rc::from(s.as_os_str()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } } } -impl AsRef for std::path::Path { - fn as_ref(&self) -> &Path { - self.into() +impl ToOwned for Path { + type Owned = PathBuf; + + fn to_owned(&self) -> PathBuf { + self.to_path_buf() + } +} + +impl AsRef for Path { + fn as_ref(&self) -> &OsStr { + self.inner.as_ref() } } @@ -773,13 +858,26 @@ impl AsRef for Path { } } -impl AsRef for Path { - fn as_ref(&self) -> &OsStr { - self.inner.as_ref() +impl AsRef for OsStr { + fn as_ref(&self) -> &Path { + Path::new(self) } } -impl AsRef for OsStr { +impl<'a> From<&'a Path> for Cow<'a, Path> { + #[inline] + fn from(s: &'a Path) -> Cow<'a, Path> { + Cow::Borrowed(s) + } +} + +impl AsRef for Cow<'_, OsStr> { + fn as_ref(&self) -> &Path { + Path::new(self) + } +} + +impl AsRef for OsString { fn as_ref(&self) -> &Path { Path::new(self) } @@ -797,16 +895,139 @@ impl AsRef for String { } } -impl AsRef for std::path::PathBuf { +impl AsRef for PathBuf { fn as_ref(&self) -> &Path { - Path::new(self) + self } } -impl std::borrow::ToOwned for Path { - type Owned = PathBuf; +impl<'a> IntoIterator for &'a PathBuf { + type Item = &'a OsStr; + type IntoIter = Iter<'a>; - fn to_owned(&self) -> PathBuf { - self.to_path_buf() + fn into_iter(self) -> Iter<'a> { + self.iter() + } +} + +impl<'a> IntoIterator for &'a Path { + type Item = &'a OsStr; + type IntoIter = Iter<'a>; + + fn into_iter(self) -> Iter<'a> { + self.iter() + } +} + +macro_rules! impl_cmp { + ($lhs:ty, $rhs: ty) => { + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + ::eq(self, other) + } + } + + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + ::eq(self, other) + } + } + + impl<'a, 'b> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + ::partial_cmp(self, other) + } + } + + impl<'a, 'b> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + ::partial_cmp(self, other) + } + } + }; +} + +impl_cmp!(PathBuf, Path); +impl_cmp!(PathBuf, &'a Path); +impl_cmp!(Cow<'a, Path>, Path); +impl_cmp!(Cow<'a, Path>, &'b Path); +impl_cmp!(Cow<'a, Path>, PathBuf); + +macro_rules! impl_cmp_os_str { + ($lhs:ty, $rhs: ty) => { + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + ::eq(self, other.as_ref()) + } + } + + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + ::eq(self.as_ref(), other) + } + } + + impl<'a, 'b> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + ::partial_cmp(self, other.as_ref()) + } + } + + impl<'a, 'b> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + ::partial_cmp(self.as_ref(), other) + } + } + }; +} + +impl_cmp_os_str!(PathBuf, OsStr); +impl_cmp_os_str!(PathBuf, &'a OsStr); +impl_cmp_os_str!(PathBuf, Cow<'a, OsStr>); +impl_cmp_os_str!(PathBuf, OsString); +impl_cmp_os_str!(Path, OsStr); +impl_cmp_os_str!(Path, &'a OsStr); +impl_cmp_os_str!(Path, Cow<'a, OsStr>); +impl_cmp_os_str!(Path, OsString); +impl_cmp_os_str!(&'a Path, OsStr); +impl_cmp_os_str!(&'a Path, Cow<'b, OsStr>); +impl_cmp_os_str!(&'a Path, OsString); + +impl<'a> From<&'a std::path::Path> for &'a Path { + fn from(path: &'a std::path::Path) -> &'a Path { + &Path::new(path.as_os_str()) + } +} + +impl<'a> Into<&'a std::path::Path> for &'a Path { + fn into(self) -> &'a std::path::Path { + std::path::Path::new(&self.inner) + } +} + +impl AsRef for Path { + fn as_ref(&self) -> &std::path::Path { + self.into() + } +} + +impl AsRef for std::path::Path { + fn as_ref(&self) -> &Path { + self.into() + } +} + +impl AsRef for std::path::PathBuf { + fn as_ref(&self) -> &Path { + let p: &std::path::Path = self.as_ref(); + p.into() } } diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index cf2e6bf..56a63a4 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -1,7 +1,12 @@ +use std::borrow::{Borrow, Cow}; use std::ffi::{OsStr, OsString}; +use std::iter::{self, FromIterator}; +use std::ops::Deref; #[cfg(feature = "unstable")] use std::pin::Pin; +use std::rc::Rc; use std::str::FromStr; +use std::sync::Arc; use crate::path::Path; #[cfg(feature = "unstable")] @@ -12,7 +17,7 @@ use crate::stream::{self, FromStream, IntoStream}; /// This struct is an async version of [`std::path::PathBuf`]. /// /// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html -#[derive(Debug, PartialEq, Default)] +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PathBuf { inner: std::path::PathBuf, } @@ -98,9 +103,9 @@ impl PathBuf { /// let mut p = PathBuf::from("/test/test.rs"); /// /// p.pop(); - /// assert_eq!(Path::new("/test"), p.as_ref()); + /// assert_eq!(Path::new("/test"), p); /// p.pop(); - /// assert_eq!(Path::new("/"), p.as_ref()); + /// assert_eq!(Path::new("/"), p); /// ``` pub fn pop(&mut self) -> bool { self.inner.pop() @@ -165,7 +170,7 @@ impl PathBuf { self.inner.set_extension(extension) } - /// Consumes the `PathBuf`, yielding its internal [`OsString`] storage. + /// Consumes the `PathBuf`, returning its internal [`OsString`] storage. /// /// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html /// @@ -191,41 +196,46 @@ impl PathBuf { } } -impl std::ops::Deref for PathBuf { - type Target = Path; - - fn deref(&self) -> &Path { - self.as_ref() +impl From> for PathBuf { + fn from(boxed: Box) -> PathBuf { + boxed.into_path_buf() } } -impl std::borrow::Borrow for PathBuf { - fn borrow(&self) -> &Path { - &**self +impl From for Box { + fn from(p: PathBuf) -> Box { + p.into_boxed_path() } } -impl From for PathBuf { - fn from(path: std::path::PathBuf) -> PathBuf { - PathBuf { inner: path } +impl Clone for Box { + #[inline] + fn clone(&self) -> Self { + self.to_path_buf().into_boxed_path() } } -impl Into for PathBuf { - fn into(self) -> std::path::PathBuf { - self.inner +impl> From<&T> for PathBuf { + fn from(s: &T) -> PathBuf { + PathBuf::from(s.as_ref().to_os_string()) } } impl From for PathBuf { - fn from(path: OsString) -> PathBuf { - std::path::PathBuf::from(path).into() + fn from(s: OsString) -> PathBuf { + PathBuf { inner: s.into() } } } -impl From<&str> for PathBuf { - fn from(path: &str) -> PathBuf { - std::path::PathBuf::from(path).into() +impl From for OsString { + fn from(path_buf: PathBuf) -> OsString { + path_buf.inner.into() + } +} + +impl From for PathBuf { + fn from(s: String) -> PathBuf { + PathBuf::from(OsString::from(s)) } } @@ -233,18 +243,77 @@ impl FromStr for PathBuf { type Err = core::convert::Infallible; fn from_str(s: &str) -> Result { - Ok(std::path::PathBuf::from(s).into()) + Ok(PathBuf::from(s)) } } -impl AsRef for PathBuf { - fn as_ref(&self) -> &Path { +impl> FromIterator

for PathBuf { + fn from_iter>(iter: I) -> PathBuf { + let mut buf = PathBuf::new(); + buf.extend(iter); + buf + } +} + +impl> iter::Extend

for PathBuf { + fn extend>(&mut self, iter: I) { + iter.into_iter().for_each(move |p| self.push(p.as_ref())); + } +} + +impl Deref for PathBuf { + type Target = Path; + + fn deref(&self) -> &Path { Path::new(&self.inner) } } -impl AsRef for PathBuf { - fn as_ref(&self) -> &std::path::Path { +impl Borrow for PathBuf { + fn borrow(&self) -> &Path { + self.deref() + } +} + +impl<'a> From for Cow<'a, Path> { + #[inline] + fn from(s: PathBuf) -> Cow<'a, Path> { + Cow::Owned(s) + } +} + +impl<'a> From<&'a PathBuf> for Cow<'a, Path> { + #[inline] + fn from(p: &'a PathBuf) -> Cow<'a, Path> { + Cow::Borrowed(p.as_path()) + } +} + +impl<'a> From> for PathBuf { + #[inline] + fn from(p: Cow<'a, Path>) -> Self { + p.into_owned() + } +} + +impl From for Arc { + #[inline] + fn from(s: PathBuf) -> Arc { + let arc: Arc = Arc::from(s.into_os_string()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) } + } +} + +impl From for Rc { + #[inline] + fn from(s: PathBuf) -> Rc { + let rc: Rc = Rc::from(s.into_os_string()); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) } + } +} + +impl AsRef for PathBuf { + fn as_ref(&self) -> &OsStr { self.inner.as_ref() } } @@ -284,16 +353,20 @@ impl<'b, P: AsRef + 'b> FromStream

for PathBuf { } } -impl> std::iter::FromIterator

for PathBuf { - fn from_iter>(iter: I) -> PathBuf { - let mut buf = PathBuf::new(); - buf.extend(iter); - buf +impl From for PathBuf { + fn from(path: std::path::PathBuf) -> PathBuf { + PathBuf { inner: path } } } -impl> std::iter::Extend

for PathBuf { - fn extend>(&mut self, iter: I) { - iter.into_iter().for_each(move |p| self.push(p.as_ref())); +impl Into for PathBuf { + fn into(self) -> std::path::PathBuf { + self.inner + } +} + +impl AsRef for PathBuf { + fn as_ref(&self) -> &std::path::Path { + self.inner.as_ref() } }