From aa13ba758ba17577f347a6da721f261b4e854409 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 15 Oct 2019 02:05:23 +0200 Subject: [PATCH] Refactor --- src/path/ancestors.rs | 39 ++ src/path/mod.rs | 4 +- src/path/path.rs | 1286 ++++++++++++++++++++--------------------- src/path/pathbuf.rs | 168 +++--- 4 files changed, 746 insertions(+), 751 deletions(-) create mode 100644 src/path/ancestors.rs diff --git a/src/path/ancestors.rs b/src/path/ancestors.rs new file mode 100644 index 0000000..c7237ff --- /dev/null +++ b/src/path/ancestors.rs @@ -0,0 +1,39 @@ +use std::iter::FusedIterator; + +use crate::path::Path; + +/// An iterator over [`Path`] and its ancestors. +/// +/// This `struct` is created by the [`ancestors`] method on [`Path`]. +/// See its documentation for more. +/// +/// # Examples +/// +/// ``` +/// use async_std::path::Path; +/// +/// let path = Path::new("/foo/bar"); +/// +/// for ancestor in path.ancestors() { +/// println!("{}", ancestor.display()); +/// } +/// ``` +/// +/// [`ancestors`]: struct.Path.html#method.ancestors +/// [`Path`]: struct.Path.html +#[derive(Copy, Clone, Debug)] +pub struct Ancestors<'a> { + pub(crate) next: Option<&'a Path>, +} + +impl<'a> Iterator for Ancestors<'a> { + type Item = &'a Path; + + fn next(&mut self) -> Option { + let next = self.next; + self.next = next.and_then(Path::parent); + next + } +} + +impl FusedIterator for Ancestors<'_> {} diff --git a/src/path/mod.rs b/src/path/mod.rs index 9b8cdc9..36b9f2f 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -4,6 +4,7 @@ //! //! [`std::path`]: https://doc.rust-lang.org/std/path/index.html +mod ancestors; mod path; mod pathbuf; @@ -23,5 +24,6 @@ pub use std::path::MAIN_SEPARATOR; #[doc(inline)] pub use std::path::is_separator; -pub use path::{Ancestors, Path}; +use ancestors::Ancestors; +pub use path::Path; pub use pathbuf::PathBuf; diff --git a/src/path/path.rs b/src/path/path.rs index 63c8659..aa15ab2 100644 --- a/src/path/path.rs +++ b/src/path/path.rs @@ -1,7 +1,6 @@ use std::ffi::OsStr; -use std::iter::FusedIterator; -use crate::path::{Components, Display, Iter, PathBuf, StripPrefixError}; +use crate::path::{Ancestors, Components, Display, Iter, PathBuf, StripPrefixError}; use crate::{fs, io}; /// This struct is an async version of [`std::path::Path`]. @@ -13,456 +12,6 @@ pub struct Path { } impl Path { - /// Produces an iterator over `Path` and its ancestors. - /// - /// The iterator will yield the `Path` that is returned if the [`parent`] method is used zero - /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`, - /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns - /// [`None`], the iterator will do likewise. The iterator will always yield at least one value, - /// namely `&self`. - /// - /// # Examples - /// - /// ``` - /// use async_std::path::Path; - /// - /// let mut ancestors = Path::new("/foo/bar").ancestors(); - /// assert_eq!(ancestors.next(), Some(Path::new("/foo/bar").into())); - /// assert_eq!(ancestors.next(), Some(Path::new("/foo").into())); - /// 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) } - } - - /// Yields the underlying [`OsStr`] slice. - /// - /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html - pub fn as_os_str(&self) -> &OsStr { - self.inner.as_os_str() - } - - /// Returns the canonical, absolute form of the path with all intermediate - /// components normalized and symbolic links resolved. - /// - /// This is an alias to [`fs::canonicalize`]. - /// - /// [`fs::canonicalize`]: ../fs/fn.canonicalize.html - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// 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")); - /// # - /// # Ok(()) }) } - /// ``` - pub async fn canonicalize(&self) -> io::Result { - fs::canonicalize(self).await - } - - /// Produces an iterator over the [`Component`]s of the path. - /// - /// When parsing the path, there is a small amount of normalization: - /// - /// * Repeated separators are ignored, so `a/b` and `a//b` both have - /// `a` and `b` as components. - /// - /// * Occurrences of `.` are normalized away, except if they are at the - /// beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and - /// `a/b` all have `a` and `b` as components, but `./a/b` starts with - /// an additional [`CurDir`] component. - /// - /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent. - /// - /// Note that no other normalization takes place; in particular, `a/c` - /// and `a/b/../c` are distinct, to account for the possibility that `b` - /// is a symbolic link (so its parent isn't `a`). - /// - /// # Examples - /// - /// ``` - /// use async_std::path::{Path, Component}; - /// use std::ffi::OsStr; - /// - /// 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) - /// ``` - /// - /// [`Component`]: enum.Component.html - /// [`CurDir`]: enum.Component.html#variant.CurDir - pub fn components(&self) -> Components<'_> { - self.inner.components() - } - - /// Returns an object that implements [`Display`] for safely printing paths - /// that may contain non-Unicode data. - /// - /// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html - /// - /// # Examples - /// - /// ``` - /// use async_std::path::Path; - /// - /// let path = Path::new("/tmp/foo.rs"); - /// - /// println!("{}", path.display()); - /// ``` - pub fn display(&self) -> Display<'_> { - self.inner.display() - } - - /// Determines whether `child` is a suffix of `self`. - /// - /// Only considers whole path components to match. - /// - /// # Examples - /// - /// ``` - /// use async_std::path::Path; - /// - /// let path = Path::new("/etc/passwd"); - /// - /// assert!(path.ends_with("passwd")); - /// ``` - pub fn ends_with>(&self, child: P) -> bool - where - P: std::convert::AsRef, - { - self.inner.ends_with(child) - } - - /// Returns `true` if the path points at an existing entity. - /// - /// This function will traverse symbolic links to query information about the - /// destination file. In case of broken symbolic links this will return `false`. - /// - /// If you cannot access the directory containing the file, e.g., because of a - /// permission error, this will return `false`. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::path::Path; - /// assert_eq!(Path::new("does_not_exist.txt").exists().await, false); - /// # - /// # Ok(()) }) } - /// ``` - /// - /// # See Also - /// - /// This is a convenience function that coerces errors to false. If you want to - /// check errors, call [fs::metadata]. - /// - /// [fs::metadata]: ../fs/fn.metadata.html - pub async fn exists(&self) -> bool { - fs::metadata(self).await.is_ok() - } - - /// Extracts the extension of [`self.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; - /// * Otherwise, the portion of the file name after the final `.` - /// - /// [`self.file_name`]: struct.Path.html#method.file_name - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use async_std::path::Path; - /// - /// let path = Path::new("foo.rs"); - /// - /// assert_eq!("rs", path.extension().unwrap()); - /// ``` - pub fn extension(&self) -> Option<&OsStr> { - self.inner.extension() - } - - /// Returns the final component of the `Path`, if there is one. - /// - /// If the path is a normal file, this is the file name. If it's the path of a directory, this - /// is the directory name. - /// - /// Returns [`None`] if the path terminates in `..`. - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use async_std::path::Path; - /// use std::ffi::OsStr; - /// - /// 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()); - /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.//").file_name()); - /// assert_eq!(None, Path::new("foo.txt/..").file_name()); - /// assert_eq!(None, Path::new("/").file_name()); - /// ``` - pub fn file_name(&self) -> Option<&OsStr> { - self.inner.file_name() - } - - /// Extracts the stem (non-extension) portion of [`self.file_name`]. - /// - /// [`self.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; - /// * Otherwise, the portion of the file name before the final `.` - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use async_std::path::Path; - /// - /// let path = Path::new("foo.rs"); - /// - /// assert_eq!("foo", path.file_stem().unwrap()); - /// ``` - pub fn file_stem(&self) -> Option<&OsStr> { - self.inner.file_stem() - } - - /// Returns `true` if the `Path` has a root. - /// - /// * 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` - /// - /// # Examples - /// - /// ``` - /// use async_std::path::Path; - /// - /// assert!(Path::new("/etc/passwd").has_root()); - /// ``` - pub fn has_root(&self) -> bool { - self.inner.has_root() - } - - /// Converts a [`Box`][`Box`] into a [`PathBuf`] without copying or - /// allocating. - /// - /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html - /// [`PathBuf`]: struct.PathBuf.html - 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) }; - inner.into_path_buf().into() - } - - /// 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. - /// - /// * 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. - /// - /// # Examples - /// - /// ``` - /// use async_std::path::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 exists on disk and is pointing at a directory. - /// - /// This function will traverse symbolic links to query information about the - /// destination file. In case of broken symbolic links this will return `false`. - /// - /// If you cannot access the directory containing the file, e.g., because of a - /// permission error, this will return `false`. - /// - /// # Examples - /// - /// ```no_run - /// # 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); - /// # - /// # Ok(()) }) } - /// ``` - /// - /// # See Also - /// - /// This is a convenience function that coerces errors to false. If you want to - /// check errors, call [fs::metadata] and handle its Result. Then call - /// [fs::Metadata::is_dir] if it was Ok. - /// - /// [fs::metadata]: ../fs/fn.metadata.html - /// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir - pub async fn is_dir(&self) -> bool { - fs::metadata(self) - .await - .map(|m| m.is_dir()) - .unwrap_or(false) - } - - /// Returns `true` if the path exists on disk and is pointing at a regular file. - /// - /// This function will traverse symbolic links to query information about the - /// destination file. In case of broken symbolic links this will return `false`. - /// - /// If you cannot access the directory containing the file, e.g., because of a - /// permission error, this will return `false`. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::path::Path; - /// assert_eq!(Path::new("./is_a_directory/").is_file().await, false); - /// assert_eq!(Path::new("a_file.txt").is_file().await, true); - /// # - /// # Ok(()) }) } - /// ``` - /// - /// # See Also - /// - /// This is a convenience function that coerces errors to false. If you want to - /// check errors, call [fs::metadata] and handle its Result. Then call - /// [fs::Metadata::is_file] if it was Ok. - /// - /// [fs::metadata]: ../fs/fn.metadata.html - /// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file - pub async fn is_file(&self) -> bool { - fs::metadata(self) - .await - .map(|m| m.is_file()) - .unwrap_or(false) - } - - /// Returns `true` if the `Path` is relative, i.e., not absolute. - /// - /// See [`is_absolute`]'s documentation for more details. - /// - /// # Examples - /// - /// ``` - /// use async_std::path::Path; - /// - /// assert!(Path::new("foo.txt").is_relative()); - /// ``` - /// - /// [`is_absolute`]: #method.is_absolute - pub fn is_relative(&self) -> bool { - self.inner.is_relative() - } - - /// Produces an iterator over the path's components viewed as [`OsStr`] - /// slices. - /// - /// For more information about the particulars of how the path is separated - /// into components, see [`components`]. - /// - /// [`components`]: #method.components - /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html - /// - /// # Examples - /// - /// ``` - /// use async_std::path::{self, Path}; - /// use std::ffi::OsStr; - /// - /// 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"))); - /// assert_eq!(it.next(), Some(OsStr::new("foo.txt"))); - /// assert_eq!(it.next(), None) - /// ``` - pub fn iter(&self) -> Iter<'_> { - self.inner.iter() - } - - /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. - /// - /// See [`PathBuf::push`] for more details on what it means to adjoin a path. - /// - /// [`PathBuf`]: struct.PathBuf.html - /// [`PathBuf::push`]: struct.PathBuf.html#method.push - /// - /// # Examples - /// - /// ``` - /// use async_std::path::{Path, PathBuf}; - /// - /// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd")); - /// ``` - pub fn join>(&self, path: P) -> PathBuf - where - P: std::convert::AsRef, - { - self.inner.join(path).into() - } - - /// Queries the file system to get information about a file, directory, etc. - /// - /// This function will traverse symbolic links to query information about the - /// destination file. - /// - /// This is an alias to [`fs::metadata`]. - /// - /// [`fs::metadata`]: ../fs/fn.metadata.html - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::path::Path; - /// - /// let path = Path::new("/Minas/tirith"); - /// let metadata = path.metadata().await.expect("metadata call failed"); - /// println!("{:?}", metadata.file_type()); - /// # - /// # Ok(()) }) } - /// ``` - pub async fn metadata(&self) -> io::Result { - fs::metadata(self).await - } - /// Directly wraps a string slice as a `Path` slice. /// /// This is a cost-free conversion. @@ -489,181 +38,11 @@ impl Path { unsafe { &*(std::path::Path::new(s) as *const std::path::Path as *const Path) } } - /// Returns the `Path` without its final component, if there is one. + /// Yields the underlying [`OsStr`] slice. /// - /// Returns [`None`] if the path terminates in a root or prefix. - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use async_std::path::Path; - /// - /// let path = Path::new("/foo/bar"); - /// let parent = path.parent().unwrap(); - /// assert_eq!(parent, Path::new("/foo")); - /// - /// let grand_parent = parent.parent().unwrap(); - /// assert_eq!(grand_parent, Path::new("/")); - /// assert_eq!(grand_parent.parent(), None); - /// ``` - pub fn parent(&self) -> Option<&Path> { - self.inner.parent().map(|p| p.into()) - } - - /// Returns an iterator over the entries within a directory. - /// - /// The iterator 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`]. - /// - /// [`io::Result`]: ../io/type.Result.html - /// [`DirEntry`]: ../fs/struct.DirEntry.html - /// [`fs::read_dir`]: ../fs/fn.read_dir.html - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::path::Path; - /// use async_std::fs; - /// use futures_util::stream::StreamExt; - /// - /// let path = Path::new("/laputa"); - /// let mut dir = fs::read_dir(&path).await.expect("read_dir call failed"); - /// while let Some(res) = dir.next().await { - /// let entry = res?; - /// println!("{}", entry.file_name().to_string_lossy()); - /// } - /// # - /// # Ok(()) }) } - /// ``` - pub async fn read_dir(&self) -> io::Result { - fs::read_dir(self).await - } - - /// Reads a symbolic link, returning the file that the link points to. - /// - /// This is an alias to [`fs::read_link`]. - /// - /// [`fs::read_link`]: ../fs/fn.read_link.html - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// 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"); - /// # - /// # Ok(()) }) } - /// ``` - pub async fn read_link(&self) -> io::Result { - fs::read_link(self).await - } - - /// Determines whether `base` is a prefix of `self`. - /// - /// Only considers whole path components to match. - /// - /// # Examples - /// - /// ``` - /// use async_std::path::Path; - /// - /// let path = Path::new("/etc/passwd"); - /// - /// assert!(path.starts_with("/etc")); - /// assert!(path.starts_with("/etc/")); - /// assert!(path.starts_with("/etc/passwd")); - /// assert!(path.starts_with("/etc/passwd/")); - /// - /// assert!(!path.starts_with("/e")); - /// ``` - pub fn starts_with>(&self, base: P) -> bool - where - P: std::convert::AsRef, - { - self.inner.starts_with(base) - } - - /// Returns a path that, when joined onto `base`, yields `self`. - /// - /// # Errors - /// - /// If `base` is not a prefix of `self` (i.e., [`starts_with`] - /// returns `false`), returns [`Err`]. - /// - /// [`starts_with`]: #method.starts_with - /// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err - /// - /// # Examples - /// - /// ``` - /// use async_std::path::{Path, PathBuf}; - /// - /// let path = Path::new("/test/haha/foo.txt"); - /// - /// assert_eq!(path.strip_prefix("/"), Ok(Path::new("test/haha/foo.txt"))); - /// assert_eq!(path.strip_prefix("/test"), Ok(Path::new("haha/foo.txt"))); - /// assert_eq!(path.strip_prefix("/test/"), Ok(Path::new("haha/foo.txt"))); - /// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Path::new(""))); - /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new(""))); - /// assert_eq!(path.strip_prefix("test").is_ok(), false); - /// assert_eq!(path.strip_prefix("/haha").is_ok(), false); - /// - /// let prefix = PathBuf::from("/test/"); - /// assert_eq!(path.strip_prefix(prefix), Ok(Path::new("haha/foo.txt"))); - /// ``` - pub fn strip_prefix

(&self, base: P) -> Result<&Path, StripPrefixError> - where - P: AsRef, - { - Ok(self.inner.strip_prefix(base)?.into()) - } - - /// Queries the metadata about a file without following symlinks. - /// - /// This is an alias to [`fs::symlink_metadata`]. - /// - /// [`fs::symlink_metadata`]: ../fs/fn.symlink_metadata.html - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::path::Path; - /// - /// let path = Path::new("/Minas/tirith"); - /// let metadata = path.symlink_metadata().await.expect("symlink_metadata call failed"); - /// println!("{:?}", metadata.file_type()); - /// # - /// # Ok(()) }) } - /// ``` - pub async fn symlink_metadata(&self) -> io::Result { - fs::symlink_metadata(self).await - } - - /// Converts a `Path` to an owned [`PathBuf`]. - /// - /// [`PathBuf`]: struct.PathBuf.html - /// - /// # Examples - /// - /// ``` - /// use async_std::path::{Path, PathBuf}; - /// - /// let path_buf = Path::new("foo.txt").to_path_buf(); - /// assert_eq!(path_buf, PathBuf::from("foo.txt")); - /// ``` - pub fn to_path_buf(&self) -> PathBuf { - PathBuf::from(self.inner.to_path_buf()) + /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html + pub fn as_os_str(&self) -> &OsStr { + self.inner.as_os_str() } /// Yields a [`&str`] slice if the `Path` is valid unicode. @@ -711,23 +90,297 @@ impl Path { self.inner.to_string_lossy() } - /// Creates an owned [`PathBuf`] like `self` but with the given extension. - /// - /// See [`PathBuf::set_extension`] for more details. + /// Converts a `Path` to an owned [`PathBuf`]. /// /// [`PathBuf`]: struct.PathBuf.html - /// [`PathBuf::set_extension`]: struct.PathBuf.html#method.set_extension /// /// # Examples /// /// ``` /// use async_std::path::{Path, PathBuf}; /// - /// let path = Path::new("foo.rs"); - /// assert_eq!(path.with_extension("txt"), PathBuf::from("foo.txt")); + /// let path_buf = Path::new("foo.txt").to_path_buf(); + /// assert_eq!(path_buf, PathBuf::from("foo.txt")); /// ``` - pub fn with_extension>(&self, extension: S) -> PathBuf { - self.inner.with_extension(extension).into() + pub fn to_path_buf(&self) -> PathBuf { + PathBuf::from(self.inner.to_path_buf()) + } + + /// 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. + /// + /// * 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. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::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. + /// + /// See [`is_absolute`]'s documentation for more details. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// assert!(Path::new("foo.txt").is_relative()); + /// ``` + /// + /// [`is_absolute`]: #method.is_absolute + pub fn is_relative(&self) -> bool { + self.inner.is_relative() + } + + /// Returns `true` if the `Path` has a root. + /// + /// * 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` + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// assert!(Path::new("/etc/passwd").has_root()); + /// ``` + pub fn has_root(&self) -> bool { + self.inner.has_root() + } + + /// Returns the `Path` without its final component, if there is one. + /// + /// Returns [`None`] if the path terminates in a root or prefix. + /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("/foo/bar"); + /// let parent = path.parent().unwrap(); + /// assert_eq!(parent, Path::new("/foo")); + /// + /// let grand_parent = parent.parent().unwrap(); + /// assert_eq!(grand_parent, Path::new("/")); + /// assert_eq!(grand_parent.parent(), None); + /// ``` + pub fn parent(&self) -> Option<&Path> { + self.inner.parent().map(|p| p.into()) + } + + /// Produces an iterator over `Path` and its ancestors. + /// + /// The iterator will yield the `Path` that is returned if the [`parent`] method is used zero + /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`, + /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns + /// [`None`], the iterator will do likewise. The iterator will always yield at least one value, + /// namely `&self`. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let mut ancestors = Path::new("/foo/bar").ancestors(); + /// assert_eq!(ancestors.next(), Some(Path::new("/foo/bar").into())); + /// assert_eq!(ancestors.next(), Some(Path::new("/foo").into())); + /// 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) } + } + + /// Returns the final component of the `Path`, if there is one. + /// + /// If the path is a normal file, this is the file name. If it's the path of a directory, this + /// is the directory name. + /// + /// Returns [`None`] if the path terminates in `..`. + /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// use std::ffi::OsStr; + /// + /// 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()); + /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.//").file_name()); + /// assert_eq!(None, Path::new("foo.txt/..").file_name()); + /// assert_eq!(None, Path::new("/").file_name()); + /// ``` + pub fn file_name(&self) -> Option<&OsStr> { + self.inner.file_name() + } + + /// Returns a path that, when joined onto `base`, yields `self`. + /// + /// # Errors + /// + /// If `base` is not a prefix of `self` (i.e., [`starts_with`] + /// returns `false`), returns [`Err`]. + /// + /// [`starts_with`]: #method.starts_with + /// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, PathBuf}; + /// + /// let path = Path::new("/test/haha/foo.txt"); + /// + /// assert_eq!(path.strip_prefix("/"), Ok(Path::new("test/haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test"), Ok(Path::new("haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test/"), Ok(Path::new("haha/foo.txt"))); + /// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Path::new(""))); + /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new(""))); + /// assert_eq!(path.strip_prefix("test").is_ok(), false); + /// assert_eq!(path.strip_prefix("/haha").is_ok(), false); + /// + /// let prefix = PathBuf::from("/test/"); + /// assert_eq!(path.strip_prefix(prefix), Ok(Path::new("haha/foo.txt"))); + /// ``` + pub fn strip_prefix

(&self, base: P) -> Result<&Path, StripPrefixError> + where + P: AsRef, + { + Ok(self.inner.strip_prefix(base.as_ref())?.into()) + } + + /// Determines whether `base` is a prefix of `self`. + /// + /// Only considers whole path components to match. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("/etc/passwd"); + /// + /// assert!(path.starts_with("/etc")); + /// assert!(path.starts_with("/etc/")); + /// assert!(path.starts_with("/etc/passwd")); + /// assert!(path.starts_with("/etc/passwd/")); + /// + /// assert!(!path.starts_with("/e")); + /// ``` + pub fn starts_with>(&self, base: P) -> bool { + self.inner.starts_with(base.as_ref()) + } + + /// Determines whether `child` is a suffix of `self`. + /// + /// Only considers whole path components to match. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("/etc/passwd"); + /// + /// assert!(path.ends_with("passwd")); + /// ``` + pub fn ends_with>(&self, child: P) -> bool { + self.inner.ends_with(child.as_ref()) + } + + /// Extracts the stem (non-extension) portion of [`self.file_name`]. + /// + /// [`self.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; + /// * Otherwise, the portion of the file name before the final `.` + /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("foo.rs"); + /// + /// assert_eq!("foo", path.file_stem().unwrap()); + /// ``` + pub fn file_stem(&self) -> Option<&OsStr> { + self.inner.file_stem() + } + + /// Extracts the extension of [`self.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; + /// * Otherwise, the portion of the file name after the final `.` + /// + /// [`self.file_name`]: struct.Path.html#method.file_name + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("foo.rs"); + /// + /// assert_eq!("rs", path.extension().unwrap()); + /// ``` + pub fn extension(&self) -> Option<&OsStr> { + self.inner.extension() + } + + /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. + /// + /// See [`PathBuf::push`] for more details on what it means to adjoin a path. + /// + /// [`PathBuf`]: struct.PathBuf.html + /// [`PathBuf::push`]: struct.PathBuf.html#method.push + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, PathBuf}; + /// + /// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd")); + /// ``` + pub fn join>(&self, path: P) -> PathBuf { + self.inner.join(path.as_ref()).into() } /// Creates an owned [`PathBuf`] like `self` but with the given file name. @@ -751,44 +404,345 @@ impl Path { pub fn with_file_name>(&self, file_name: S) -> PathBuf { self.inner.with_file_name(file_name).into() } -} -/// An iterator over [`Path`] and its ancestors. -/// -/// This `struct` is created by the [`ancestors`] method on [`Path`]. -/// See its documentation for more. -/// -/// # Examples -/// -/// ``` -/// use async_std::path::Path; -/// -/// let path = Path::new("/foo/bar"); -/// -/// for ancestor in path.ancestors() { -/// println!("{}", ancestor.display()); -/// } -/// ``` -/// -/// [`ancestors`]: struct.Path.html#method.ancestors -/// [`Path`]: struct.Path.html -#[derive(Copy, Clone, Debug)] -pub struct Ancestors<'a> { - next: Option<&'a Path>, -} + /// Creates an owned [`PathBuf`] like `self` but with the given extension. + /// + /// See [`PathBuf::set_extension`] for more details. + /// + /// [`PathBuf`]: struct.PathBuf.html + /// [`PathBuf::set_extension`]: struct.PathBuf.html#method.set_extension + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, PathBuf}; + /// + /// let path = Path::new("foo.rs"); + /// assert_eq!(path.with_extension("txt"), PathBuf::from("foo.txt")); + /// ``` + pub fn with_extension>(&self, extension: S) -> PathBuf { + self.inner.with_extension(extension).into() + } -impl<'a> Iterator for Ancestors<'a> { - type Item = &'a Path; + /// Produces an iterator over the [`Component`]s of the path. + /// + /// When parsing the path, there is a small amount of normalization: + /// + /// * Repeated separators are ignored, so `a/b` and `a//b` both have + /// `a` and `b` as components. + /// + /// * Occurrences of `.` are normalized away, except if they are at the + /// beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and + /// `a/b` all have `a` and `b` as components, but `./a/b` starts with + /// an additional [`CurDir`] component. + /// + /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent. + /// + /// Note that no other normalization takes place; in particular, `a/c` + /// and `a/b/../c` are distinct, to account for the possibility that `b` + /// is a symbolic link (so its parent isn't `a`). + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, Component}; + /// use std::ffi::OsStr; + /// + /// 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) + /// ``` + /// + /// [`Component`]: enum.Component.html + /// [`CurDir`]: enum.Component.html#variant.CurDir + pub fn components(&self) -> Components<'_> { + self.inner.components() + } - fn next(&mut self) -> Option { - let next = self.next; - self.next = next.and_then(Path::parent); - next + /// Produces an iterator over the path's components viewed as [`OsStr`] + /// slices. + /// + /// For more information about the particulars of how the path is separated + /// into components, see [`components`]. + /// + /// [`components`]: #method.components + /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{self, Path}; + /// use std::ffi::OsStr; + /// + /// 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"))); + /// assert_eq!(it.next(), Some(OsStr::new("foo.txt"))); + /// assert_eq!(it.next(), None) + /// ``` + pub fn iter(&self) -> Iter<'_> { + self.inner.iter() + } + + /// Returns an object that implements [`Display`] for safely printing paths + /// that may contain non-Unicode data. + /// + /// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html + /// + /// # Examples + /// + /// ``` + /// use async_std::path::Path; + /// + /// let path = Path::new("/tmp/foo.rs"); + /// + /// println!("{}", path.display()); + /// ``` + pub fn display(&self) -> Display<'_> { + self.inner.display() + } + + /// Queries the file system to get information about a file, directory, etc. + /// + /// This function will traverse symbolic links to query information about the + /// destination file. + /// + /// This is an alias to [`fs::metadata`]. + /// + /// [`fs::metadata`]: ../fs/fn.metadata.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::path::Path; + /// + /// let path = Path::new("/Minas/tirith"); + /// let metadata = path.metadata().await.expect("metadata call failed"); + /// println!("{:?}", metadata.file_type()); + /// # + /// # Ok(()) }) } + /// ``` + pub async fn metadata(&self) -> io::Result { + fs::metadata(self).await + } + + /// Queries the metadata about a file without following symlinks. + /// + /// This is an alias to [`fs::symlink_metadata`]. + /// + /// [`fs::symlink_metadata`]: ../fs/fn.symlink_metadata.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::path::Path; + /// + /// let path = Path::new("/Minas/tirith"); + /// let metadata = path.symlink_metadata().await.expect("symlink_metadata call failed"); + /// println!("{:?}", metadata.file_type()); + /// # + /// # Ok(()) }) } + /// ``` + pub async fn symlink_metadata(&self) -> io::Result { + fs::symlink_metadata(self).await + } + + /// Returns the canonical, absolute form of the path with all intermediate + /// components normalized and symbolic links resolved. + /// + /// This is an alias to [`fs::canonicalize`]. + /// + /// [`fs::canonicalize`]: ../fs/fn.canonicalize.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// 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")); + /// # + /// # Ok(()) }) } + /// ``` + pub async fn canonicalize(&self) -> io::Result { + fs::canonicalize(self).await + } + + /// Reads a symbolic link, returning the file that the link points to. + /// + /// This is an alias to [`fs::read_link`]. + /// + /// [`fs::read_link`]: ../fs/fn.read_link.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// 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"); + /// # + /// # Ok(()) }) } + /// ``` + pub async fn read_link(&self) -> io::Result { + fs::read_link(self).await + } + + /// Returns an iterator over the entries within a directory. + /// + /// The iterator 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`]. + /// + /// [`io::Result`]: ../io/type.Result.html + /// [`DirEntry`]: ../fs/struct.DirEntry.html + /// [`fs::read_dir`]: ../fs/fn.read_dir.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::path::Path; + /// use async_std::fs; + /// use futures_util::stream::StreamExt; + /// + /// let path = Path::new("/laputa"); + /// let mut dir = fs::read_dir(&path).await.expect("read_dir call failed"); + /// while let Some(res) = dir.next().await { + /// let entry = res?; + /// println!("{}", entry.file_name().to_string_lossy()); + /// } + /// # + /// # Ok(()) }) } + /// ``` + pub async fn read_dir(&self) -> io::Result { + fs::read_dir(self).await + } + + /// Returns `true` if the path points at an existing entity. + /// + /// This function will traverse symbolic links to query information about the + /// destination file. In case of broken symbolic links this will return `false`. + /// + /// If you cannot access the directory containing the file, e.g., because of a + /// permission error, this will return `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::path::Path; + /// assert_eq!(Path::new("does_not_exist.txt").exists().await, false); + /// # + /// # Ok(()) }) } + /// ``` + /// + /// # See Also + /// + /// This is a convenience function that coerces errors to false. If you want to + /// check errors, call [fs::metadata]. + /// + /// [fs::metadata]: ../fs/fn.metadata.html + pub async fn exists(&self) -> bool { + fs::metadata(self).await.is_ok() + } + + /// Returns `true` if the path exists on disk and is pointing at a regular file. + /// + /// This function will traverse symbolic links to query information about the + /// destination file. In case of broken symbolic links this will return `false`. + /// + /// If you cannot access the directory containing the file, e.g., because of a + /// permission error, this will return `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::path::Path; + /// assert_eq!(Path::new("./is_a_directory/").is_file().await, false); + /// assert_eq!(Path::new("a_file.txt").is_file().await, true); + /// # + /// # Ok(()) }) } + /// ``` + /// + /// # See Also + /// + /// This is a convenience function that coerces errors to false. If you want to + /// check errors, call [fs::metadata] and handle its Result. Then call + /// [fs::Metadata::is_file] if it was Ok. + /// + /// [fs::metadata]: ../fs/fn.metadata.html + /// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file + pub async fn is_file(&self) -> bool { + fs::metadata(self) + .await + .map(|m| m.is_file()) + .unwrap_or(false) + } + + /// Returns `true` if the path exists on disk and is pointing at a directory. + /// + /// This function will traverse symbolic links to query information about the + /// destination file. In case of broken symbolic links this will return `false`. + /// + /// If you cannot access the directory containing the file, e.g., because of a + /// permission error, this will return `false`. + /// + /// # Examples + /// + /// ```no_run + /// # 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); + /// # + /// # Ok(()) }) } + /// ``` + /// + /// # See Also + /// + /// This is a convenience function that coerces errors to false. If you want to + /// check errors, call [fs::metadata] and handle its Result. Then call + /// [fs::Metadata::is_dir] if it was Ok. + /// + /// [fs::metadata]: ../fs/fn.metadata.html + /// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir + pub async fn is_dir(&self) -> bool { + fs::metadata(self) + .await + .map(|m| m.is_dir()) + .unwrap_or(false) + } + + /// Converts a [`Box`][`Box`] into a [`PathBuf`] without copying or + /// allocating. + /// + /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html + /// [`PathBuf`]: struct.PathBuf.html + 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) }; + inner.into_path_buf().into() } } -impl FusedIterator for Ancestors<'_> {} - 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()) diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs index 28062b2..64744e1 100644 --- a/src/path/pathbuf.rs +++ b/src/path/pathbuf.rs @@ -11,6 +11,19 @@ pub struct PathBuf { } impl PathBuf { + /// Allocates an empty `PathBuf`. + /// + /// # Examples + /// + /// ``` + /// use async_std::path::PathBuf; + /// + /// let path = PathBuf::new(); + /// ``` + pub fn new() -> PathBuf { + std::path::PathBuf::new().into() + } + /// Coerces to a [`Path`] slice. /// /// [`Path`]: struct.Path.html @@ -27,68 +40,6 @@ impl PathBuf { self.inner.as_path().into() } - /// Converts this `PathBuf` into a [boxed][`Box`] [`Path`]. - /// - /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html - /// [`Path`]: struct.Path.html - pub fn into_boxed_path(self) -> Box { - let rw = Box::into_raw(self.inner.into_boxed_path()) as *mut Path; - unsafe { Box::from_raw(rw) } - } - - /// Consumes the `PathBuf`, yielding its internal [`OsString`] storage. - /// - /// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html - /// - /// # Examples - /// - /// ``` - /// use async_std::path::PathBuf; - /// - /// let p = PathBuf::from("/the/head"); - /// let os_str = p.into_os_string(); - /// ``` - pub fn into_os_string(self) -> OsString { - self.inner.into_os_string() - } - - /// Allocates an empty `PathBuf`. - /// - /// # Examples - /// - /// ``` - /// use async_std::path::PathBuf; - /// - /// let path = PathBuf::new(); - /// ``` - pub fn new() -> PathBuf { - std::path::PathBuf::new().into() - } - - /// Truncates `self` to [`self.parent`]. - /// - /// Returns `false` and does nothing if [`self.parent`] is [`None`]. - /// Otherwise, returns `true`. - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// [`self.parent`]: struct.PathBuf.html#method.parent - /// - /// # Examples - /// - /// ``` - /// use async_std::path::{Path, PathBuf}; - /// - /// let mut p = PathBuf::from("/test/test.rs"); - /// - /// p.pop(); - /// assert_eq!(Path::new("/test"), p.as_ref()); - /// p.pop(); - /// assert_eq!(Path::new("/"), p.as_ref()); - /// ``` - pub fn pop(&mut self) -> bool { - self.inner.pop() - } - /// Extends `self` with `path`. /// /// If `path` is absolute, it replaces the current path. @@ -120,8 +71,62 @@ impl PathBuf { /// path.push("/etc"); /// assert_eq!(path, PathBuf::from("/etc")); /// ``` - pub fn push>(&mut self, path: P) { - self.inner.push(path) + pub fn push>(&mut self, path: P) { + self.inner.push(path.as_ref()) + } + + /// Truncates `self` to [`self.parent`]. + /// + /// Returns `false` and does nothing if [`self.parent`] is [`None`]. + /// Otherwise, returns `true`. + /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// [`self.parent`]: struct.PathBuf.html#method.parent + /// + /// # Examples + /// + /// ``` + /// use async_std::path::{Path, PathBuf}; + /// + /// let mut p = PathBuf::from("/test/test.rs"); + /// + /// p.pop(); + /// assert_eq!(Path::new("/test"), p.as_ref()); + /// p.pop(); + /// assert_eq!(Path::new("/"), p.as_ref()); + /// ``` + pub fn pop(&mut self) -> bool { + self.inner.pop() + } + + /// Updates [`self.file_name`] to `file_name`. + /// + /// If [`self.file_name`] was [`None`], this is equivalent to pushing + /// `file_name`. + /// + /// Otherwise it is equivalent to calling [`pop`] and then pushing + /// `file_name`. The new path will be a sibling of the original path. + /// (That is, it will have the same parent.) + /// + /// [`self.file_name`]: struct.PathBuf.html#method.file_name + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// [`pop`]: struct.PathBuf.html#method.pop + /// + /// # Examples + /// + /// ``` + /// use async_std::path::PathBuf; + /// + /// let mut buf = PathBuf::from("/"); + /// assert!(buf.file_name() == None); + /// buf.set_file_name("bar"); + /// assert!(buf == PathBuf::from("/bar")); + /// assert!(buf.file_name().is_some()); + /// buf.set_file_name("baz.txt"); + /// assert!(buf == PathBuf::from("/baz.txt")); + /// ``` + pub fn set_file_name>(&mut self, file_name: S) { + self.inner.set_file_name(file_name) } /// Updates [`self.extension`] to `extension`. @@ -153,34 +158,29 @@ impl PathBuf { self.inner.set_extension(extension) } - /// Updates [`self.file_name`] to `file_name`. + /// Consumes the `PathBuf`, yielding its internal [`OsString`] storage. /// - /// If [`self.file_name`] was [`None`], this is equivalent to pushing - /// `file_name`. - /// - /// Otherwise it is equivalent to calling [`pop`] and then pushing - /// `file_name`. The new path will be a sibling of the original path. - /// (That is, it will have the same parent.) - /// - /// [`self.file_name`]: struct.PathBuf.html#method.file_name - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// [`pop`]: struct.PathBuf.html#method.pop + /// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html /// /// # Examples /// /// ``` /// use async_std::path::PathBuf; /// - /// let mut buf = PathBuf::from("/"); - /// assert!(buf.file_name() == None); - /// buf.set_file_name("bar"); - /// assert!(buf == PathBuf::from("/bar")); - /// assert!(buf.file_name().is_some()); - /// buf.set_file_name("baz.txt"); - /// assert!(buf == PathBuf::from("/baz.txt")); + /// let p = PathBuf::from("/the/head"); + /// let os_str = p.into_os_string(); /// ``` - pub fn set_file_name>(&mut self, file_name: S) { - self.inner.set_file_name(file_name) + pub fn into_os_string(self) -> OsString { + self.inner.into_os_string() + } + + /// Converts this `PathBuf` into a [boxed][`Box`] [`Path`]. + /// + /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html + /// [`Path`]: struct.Path.html + pub fn into_boxed_path(self) -> Box { + let rw = Box::into_raw(self.inner.into_boxed_path()) as *mut Path; + unsafe { Box::from_raw(rw) } } }