You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
378 lines
9.6 KiB
Rust
378 lines
9.6 KiB
Rust
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")]
|
|
use crate::prelude::*;
|
|
#[cfg(feature = "unstable")]
|
|
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(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct PathBuf {
|
|
inner: std::path::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
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use async_std::path::{Path, PathBuf};
|
|
///
|
|
/// let p = PathBuf::from("/test");
|
|
/// assert_eq!(Path::new("/test"), p.as_path());
|
|
/// ```
|
|
pub fn as_path(&self) -> &Path {
|
|
self.inner.as_path().into()
|
|
}
|
|
|
|
/// Extends `self` with `path`.
|
|
///
|
|
/// If `path` is absolute, it replaces the current path.
|
|
///
|
|
/// On Windows:
|
|
///
|
|
/// * if `path` has a root but no prefix (e.g., `\windows`), it
|
|
/// replaces everything except for the prefix (if any) of `self`.
|
|
/// * if `path` has a prefix but no root, it replaces `self`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// Pushing a relative path extends the existing path:
|
|
///
|
|
/// ```
|
|
/// use async_std::path::PathBuf;
|
|
///
|
|
/// let mut path = PathBuf::from("/tmp");
|
|
/// path.push("file.bk");
|
|
/// assert_eq!(path, PathBuf::from("/tmp/file.bk"));
|
|
/// ```
|
|
///
|
|
/// Pushing an absolute path replaces the existing path:
|
|
///
|
|
/// ```
|
|
/// use async_std::path::PathBuf;
|
|
///
|
|
/// let mut path = PathBuf::from("/tmp");
|
|
/// path.push("/etc");
|
|
/// assert_eq!(path, PathBuf::from("/etc"));
|
|
/// ```
|
|
pub fn push<P: AsRef<Path>>(&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);
|
|
/// p.pop();
|
|
/// assert_eq!(Path::new("/"), p);
|
|
/// ```
|
|
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<S: AsRef<OsStr>>(&mut self, file_name: S) {
|
|
self.inner.set_file_name(file_name)
|
|
}
|
|
|
|
/// Updates [`self.extension`] to `extension`.
|
|
///
|
|
/// Returns `false` and does nothing if [`self.file_name`] is [`None`],
|
|
/// returns `true` and updates the extension otherwise.
|
|
///
|
|
/// If [`self.extension`] is [`None`], the extension is added; otherwise
|
|
/// it is replaced.
|
|
///
|
|
/// [`self.file_name`]: struct.PathBuf.html#method.file_name
|
|
/// [`self.extension`]: struct.PathBuf.html#method.extension
|
|
/// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use async_std::path::{Path, PathBuf};
|
|
///
|
|
/// let mut p = PathBuf::from("/feel/the");
|
|
///
|
|
/// p.set_extension("force");
|
|
/// assert_eq!(Path::new("/feel/the.force"), p.as_path());
|
|
///
|
|
/// p.set_extension("dark_side");
|
|
/// assert_eq!(Path::new("/feel/the.dark_side"), p.as_path());
|
|
/// ```
|
|
pub fn set_extension<S: AsRef<OsStr>>(&mut self, extension: S) -> bool {
|
|
self.inner.set_extension(extension)
|
|
}
|
|
|
|
/// Consumes the `PathBuf`, returning 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()
|
|
}
|
|
|
|
/// 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<Path> {
|
|
let rw = Box::into_raw(self.inner.into_boxed_path()) as *mut Path;
|
|
unsafe { Box::from_raw(rw) }
|
|
}
|
|
}
|
|
|
|
impl From<Box<Path>> for PathBuf {
|
|
fn from(boxed: Box<Path>) -> PathBuf {
|
|
boxed.into_path_buf()
|
|
}
|
|
}
|
|
|
|
impl From<PathBuf> for Box<Path> {
|
|
fn from(p: PathBuf) -> Box<Path> {
|
|
p.into_boxed_path()
|
|
}
|
|
}
|
|
|
|
impl Clone for Box<Path> {
|
|
#[inline]
|
|
fn clone(&self) -> Self {
|
|
self.to_path_buf().into_boxed_path()
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + AsRef<OsStr>> From<&T> for PathBuf {
|
|
fn from(s: &T) -> PathBuf {
|
|
PathBuf::from(s.as_ref().to_os_string())
|
|
}
|
|
}
|
|
|
|
impl From<OsString> for PathBuf {
|
|
fn from(s: OsString) -> PathBuf {
|
|
PathBuf { inner: s.into() }
|
|
}
|
|
}
|
|
|
|
impl From<PathBuf> for OsString {
|
|
fn from(path_buf: PathBuf) -> OsString {
|
|
path_buf.inner.into()
|
|
}
|
|
}
|
|
|
|
impl From<String> for PathBuf {
|
|
fn from(s: String) -> PathBuf {
|
|
PathBuf::from(OsString::from(s))
|
|
}
|
|
}
|
|
|
|
impl FromStr for PathBuf {
|
|
type Err = core::convert::Infallible;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(PathBuf::from(s))
|
|
}
|
|
}
|
|
|
|
impl<P: AsRef<Path>> FromIterator<P> for PathBuf {
|
|
fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> PathBuf {
|
|
let mut buf = PathBuf::new();
|
|
buf.extend(iter);
|
|
buf
|
|
}
|
|
}
|
|
|
|
impl<P: AsRef<Path>> iter::Extend<P> for PathBuf {
|
|
fn extend<I: IntoIterator<Item = P>>(&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 Borrow<Path> for PathBuf {
|
|
fn borrow(&self) -> &Path {
|
|
self.deref()
|
|
}
|
|
}
|
|
|
|
impl<'a> From<PathBuf> 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<Cow<'a, Path>> for PathBuf {
|
|
#[inline]
|
|
fn from(p: Cow<'a, Path>) -> Self {
|
|
p.into_owned()
|
|
}
|
|
}
|
|
|
|
impl From<PathBuf> for Arc<Path> {
|
|
#[inline]
|
|
fn from(s: PathBuf) -> Arc<Path> {
|
|
let arc: Arc<OsStr> = Arc::from(s.into_os_string());
|
|
unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) }
|
|
}
|
|
}
|
|
|
|
impl From<PathBuf> for Rc<Path> {
|
|
#[inline]
|
|
fn from(s: PathBuf) -> Rc<Path> {
|
|
let rc: Rc<OsStr> = Rc::from(s.into_os_string());
|
|
unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) }
|
|
}
|
|
}
|
|
|
|
impl AsRef<OsStr> for PathBuf {
|
|
fn as_ref(&self) -> &OsStr {
|
|
self.inner.as_ref()
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "unstable")]
|
|
impl<P: AsRef<Path>> stream::Extend<P> for PathBuf {
|
|
fn extend<'a, S: IntoStream<Item = P> + 'a>(
|
|
&'a mut self,
|
|
stream: S,
|
|
) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
|
|
where
|
|
<S as IntoStream>::IntoStream: Send,
|
|
{
|
|
let stream = stream.into_stream();
|
|
|
|
Box::pin(async move {
|
|
pin_utils::pin_mut!(stream);
|
|
|
|
while let Some(item) = stream.next().await {
|
|
self.push(item.as_ref());
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "unstable")]
|
|
impl<'b, P: AsRef<Path> + 'b + Send> FromStream<P> for PathBuf {
|
|
#[inline]
|
|
fn from_stream<'a, S: IntoStream<Item = P> + 'a>(
|
|
stream: S,
|
|
) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
|
|
where
|
|
<S as IntoStream>::IntoStream: Send,
|
|
{
|
|
let stream = stream.into_stream();
|
|
|
|
Box::pin(async move {
|
|
let mut out = Self::new();
|
|
stream::extend(&mut out, stream).await;
|
|
out
|
|
})
|
|
}
|
|
}
|
|
|
|
impl From<std::path::PathBuf> for PathBuf {
|
|
fn from(path: std::path::PathBuf) -> PathBuf {
|
|
PathBuf { inner: path }
|
|
}
|
|
}
|
|
|
|
impl Into<std::path::PathBuf> for PathBuf {
|
|
fn into(self) -> std::path::PathBuf {
|
|
self.inner
|
|
}
|
|
}
|
|
|
|
impl AsRef<std::path::Path> for PathBuf {
|
|
fn as_ref(&self) -> &std::path::Path {
|
|
self.inner.as_ref()
|
|
}
|
|
}
|