Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/paths/src/lib.rs')
| -rw-r--r-- | crates/paths/src/lib.rs | 242 |
1 files changed, 163 insertions, 79 deletions
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index a63d251c20..2d3653401d 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs @@ -1,4 +1,4 @@ -//! Thin wrappers around `std::path`, distinguishing between absolute and +//! Thin wrappers around `std::path`/`camino::path`, distinguishing between absolute and //! relative paths. #![warn(rust_2018_idioms, unused_lifetimes)] @@ -7,16 +7,24 @@ use std::{ borrow::Borrow, ffi::OsStr, fmt, ops, - path::{Component, Path, PathBuf}, + path::{Path, PathBuf}, }; -/// Wrapper around an absolute [`PathBuf`]. -#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub struct AbsPathBuf(PathBuf); +pub use camino::*; + +/// Wrapper around an absolute [`Utf8PathBuf`]. +#[derive(Debug, Clone, Ord, PartialOrd, Eq, Hash)] +pub struct AbsPathBuf(Utf8PathBuf); + +impl From<AbsPathBuf> for Utf8PathBuf { + fn from(AbsPathBuf(path_buf): AbsPathBuf) -> Utf8PathBuf { + path_buf + } +} impl From<AbsPathBuf> for PathBuf { fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf { - path_buf + path_buf.into() } } @@ -27,9 +35,21 @@ impl ops::Deref for AbsPathBuf { } } +impl AsRef<Utf8Path> for AbsPathBuf { + fn as_ref(&self) -> &Utf8Path { + self.0.as_path() + } +} + +impl AsRef<OsStr> for AbsPathBuf { + fn as_ref(&self) -> &OsStr { + self.0.as_ref() + } +} + impl AsRef<Path> for AbsPathBuf { fn as_ref(&self) -> &Path { - self.0.as_path() + self.0.as_ref() } } @@ -45,26 +65,36 @@ impl Borrow<AbsPath> for AbsPathBuf { } } +impl TryFrom<Utf8PathBuf> for AbsPathBuf { + type Error = Utf8PathBuf; + fn try_from(path_buf: Utf8PathBuf) -> Result<AbsPathBuf, Utf8PathBuf> { + if !path_buf.is_absolute() { + return Err(path_buf); + } + Ok(AbsPathBuf(path_buf)) + } +} + impl TryFrom<PathBuf> for AbsPathBuf { type Error = PathBuf; fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> { if !path_buf.is_absolute() { return Err(path_buf); } - Ok(AbsPathBuf(path_buf)) + Ok(AbsPathBuf(Utf8PathBuf::from_path_buf(path_buf)?)) } } impl TryFrom<&str> for AbsPathBuf { - type Error = PathBuf; - fn try_from(path: &str) -> Result<AbsPathBuf, PathBuf> { - AbsPathBuf::try_from(PathBuf::from(path)) + type Error = Utf8PathBuf; + fn try_from(path: &str) -> Result<AbsPathBuf, Utf8PathBuf> { + AbsPathBuf::try_from(Utf8PathBuf::from(path)) } } -impl PartialEq<AbsPath> for AbsPathBuf { - fn eq(&self, other: &AbsPath) -> bool { - self.as_path() == other +impl<P: AsRef<Path> + ?Sized> PartialEq<P> for AbsPathBuf { + fn eq(&self, other: &P) -> bool { + self.0.as_std_path() == other.as_ref() } } @@ -74,19 +104,31 @@ impl AbsPathBuf { /// # Panics /// /// Panics if `path` is not absolute. - pub fn assert(path: PathBuf) -> AbsPathBuf { + pub fn assert(path: Utf8PathBuf) -> AbsPathBuf { AbsPathBuf::try_from(path) - .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display())) + .unwrap_or_else(|path| panic!("expected absolute path, got {}", path)) + } + + /// Wrap the given absolute path in `AbsPathBuf` + /// + /// # Panics + /// + /// Panics if `path` is not absolute. + pub fn assert_utf8(path: PathBuf) -> AbsPathBuf { + AbsPathBuf::assert( + Utf8PathBuf::from_path_buf(path) + .unwrap_or_else(|path| panic!("expected utf8 path, got {}", path.display())), + ) } /// Coerces to an `AbsPath` slice. /// - /// Equivalent of [`PathBuf::as_path`] for `AbsPathBuf`. + /// Equivalent of [`Utf8PathBuf::as_path`] for `AbsPathBuf`. pub fn as_path(&self) -> &AbsPath { AbsPath::assert(self.0.as_path()) } - /// Equivalent of [`PathBuf::pop`] for `AbsPathBuf`. + /// Equivalent of [`Utf8PathBuf::pop`] for `AbsPathBuf`. /// /// Note that this won't remove the root component, so `self` will still be /// absolute. @@ -97,18 +139,36 @@ impl AbsPathBuf { impl fmt::Display for AbsPathBuf { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0.display(), f) + fmt::Display::fmt(&self.0, f) } } -/// Wrapper around an absolute [`Path`]. -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +/// Wrapper around an absolute [`Utf8Path`]. +#[derive(Debug, Ord, PartialOrd, Eq, Hash)] #[repr(transparent)] -pub struct AbsPath(Path); +pub struct AbsPath(Utf8Path); + +impl<P: AsRef<Path> + ?Sized> PartialEq<P> for AbsPath { + fn eq(&self, other: &P) -> bool { + self.0.as_std_path() == other.as_ref() + } +} + +impl AsRef<Utf8Path> for AbsPath { + fn as_ref(&self) -> &Utf8Path { + &self.0 + } +} impl AsRef<Path> for AbsPath { fn as_ref(&self) -> &Path { - &self.0 + self.0.as_ref() + } +} + +impl AsRef<OsStr> for AbsPath { + fn as_ref(&self) -> &OsStr { + self.0.as_ref() } } @@ -120,9 +180,9 @@ impl ToOwned for AbsPath { } } -impl<'a> TryFrom<&'a Path> for &'a AbsPath { - type Error = &'a Path; - fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> { +impl<'a> TryFrom<&'a Utf8Path> for &'a AbsPath { + type Error = &'a Utf8Path; + fn try_from(path: &'a Utf8Path) -> Result<&'a AbsPath, &'a Utf8Path> { if !path.is_absolute() { return Err(path); } @@ -136,24 +196,24 @@ impl AbsPath { /// # Panics /// /// Panics if `path` is not absolute. - pub fn assert(path: &Path) -> &AbsPath { + pub fn assert(path: &Utf8Path) -> &AbsPath { assert!(path.is_absolute()); - unsafe { &*(path as *const Path as *const AbsPath) } + unsafe { &*(path as *const Utf8Path as *const AbsPath) } } - /// Equivalent of [`Path::parent`] for `AbsPath`. + /// Equivalent of [`Utf8Path::parent`] for `AbsPath`. pub fn parent(&self) -> Option<&AbsPath> { self.0.parent().map(AbsPath::assert) } - /// Equivalent of [`Path::join`] for `AbsPath` with an additional normalize step afterwards. - pub fn absolutize(&self, path: impl AsRef<Path>) -> AbsPathBuf { + /// Equivalent of [`Utf8Path::join`] for `AbsPath` with an additional normalize step afterwards. + pub fn absolutize(&self, path: impl AsRef<Utf8Path>) -> AbsPathBuf { self.join(path).normalize() } - /// Equivalent of [`Path::join`] for `AbsPath`. - pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf { - self.as_ref().join(path).try_into().unwrap() + /// Equivalent of [`Utf8Path::join`] for `AbsPath`. + pub fn join(&self, path: impl AsRef<Utf8Path>) -> AbsPathBuf { + Utf8Path::join(self.as_ref(), path).try_into().unwrap() } /// Normalize the given path: @@ -172,7 +232,7 @@ impl AbsPath { AbsPathBuf(normalize_path(&self.0)) } - /// Equivalent of [`Path::to_path_buf`] for `AbsPath`. + /// Equivalent of [`Utf8Path::to_path_buf`] for `AbsPath`. pub fn to_path_buf(&self) -> AbsPathBuf { AbsPathBuf::try_from(self.0.to_path_buf()).unwrap() } @@ -181,7 +241,7 @@ impl AbsPath { panic!("We explicitly do not provide canonicalization API, as that is almost always a wrong solution, see #14430") } - /// Equivalent of [`Path::strip_prefix`] for `AbsPath`. + /// Equivalent of [`Utf8Path::strip_prefix`] for `AbsPath`. /// /// Returns a relative path. pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> { @@ -195,57 +255,61 @@ impl AbsPath { } pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { - Some(( - self.file_stem()?.to_str()?, - self.extension().and_then(|extension| extension.to_str()), - )) + Some((self.file_stem()?, self.extension())) } // region:delegate-methods - // Note that we deliberately don't implement `Deref<Target = Path>` here. + // Note that we deliberately don't implement `Deref<Target = Utf8Path>` here. // - // The problem with `Path` is that it directly exposes convenience IO-ing - // methods. For example, `Path::exists` delegates to `fs::metadata`. + // The problem with `Utf8Path` is that it directly exposes convenience IO-ing + // methods. For example, `Utf8Path::exists` delegates to `fs::metadata`. // // For `AbsPath`, we want to make sure that this is a POD type, and that all // IO goes via `fs`. That way, it becomes easier to mock IO when we need it. - pub fn file_name(&self) -> Option<&OsStr> { + pub fn file_name(&self) -> Option<&str> { self.0.file_name() } - pub fn extension(&self) -> Option<&OsStr> { + pub fn extension(&self) -> Option<&str> { self.0.extension() } - pub fn file_stem(&self) -> Option<&OsStr> { + pub fn file_stem(&self) -> Option<&str> { self.0.file_stem() } pub fn as_os_str(&self) -> &OsStr { self.0.as_os_str() } + pub fn as_str(&self) -> &str { + self.0.as_str() + } #[deprecated(note = "use Display instead")] - pub fn display(&self) -> std::path::Display<'_> { - self.0.display() + pub fn display(&self) -> ! { + unimplemented!() } #[deprecated(note = "use std::fs::metadata().is_ok() instead")] - pub fn exists(&self) -> bool { - self.0.exists() + pub fn exists(&self) -> ! { + unimplemented!() + } + + pub fn components(&self) -> Utf8Components<'_> { + self.0.components() } // endregion:delegate-methods } impl fmt::Display for AbsPath { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0.display(), f) + fmt::Display::fmt(&self.0, f) } } -/// Wrapper around a relative [`PathBuf`]. +/// Wrapper around a relative [`Utf8PathBuf`]. #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub struct RelPathBuf(PathBuf); +pub struct RelPathBuf(Utf8PathBuf); -impl From<RelPathBuf> for PathBuf { - fn from(RelPathBuf(path_buf): RelPathBuf) -> PathBuf { +impl From<RelPathBuf> for Utf8PathBuf { + fn from(RelPathBuf(path_buf): RelPathBuf) -> Utf8PathBuf { path_buf } } @@ -257,15 +321,21 @@ impl ops::Deref for RelPathBuf { } } +impl AsRef<Utf8Path> for RelPathBuf { + fn as_ref(&self) -> &Utf8Path { + self.0.as_path() + } +} + impl AsRef<Path> for RelPathBuf { fn as_ref(&self) -> &Path { - self.0.as_path() + self.0.as_ref() } } -impl TryFrom<PathBuf> for RelPathBuf { - type Error = PathBuf; - fn try_from(path_buf: PathBuf) -> Result<RelPathBuf, PathBuf> { +impl TryFrom<Utf8PathBuf> for RelPathBuf { + type Error = Utf8PathBuf; + fn try_from(path_buf: Utf8PathBuf) -> Result<RelPathBuf, Utf8PathBuf> { if !path_buf.is_relative() { return Err(path_buf); } @@ -274,65 +344,79 @@ impl TryFrom<PathBuf> for RelPathBuf { } impl TryFrom<&str> for RelPathBuf { - type Error = PathBuf; - fn try_from(path: &str) -> Result<RelPathBuf, PathBuf> { - RelPathBuf::try_from(PathBuf::from(path)) + type Error = Utf8PathBuf; + fn try_from(path: &str) -> Result<RelPathBuf, Utf8PathBuf> { + RelPathBuf::try_from(Utf8PathBuf::from(path)) } } impl RelPathBuf { /// Coerces to a `RelPath` slice. /// - /// Equivalent of [`PathBuf::as_path`] for `RelPathBuf`. + /// Equivalent of [`Utf8PathBuf::as_path`] for `RelPathBuf`. pub fn as_path(&self) -> &RelPath { RelPath::new_unchecked(self.0.as_path()) } } -/// Wrapper around a relative [`Path`]. +/// Wrapper around a relative [`Utf8Path`]. #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] #[repr(transparent)] -pub struct RelPath(Path); +pub struct RelPath(Utf8Path); + +impl AsRef<Utf8Path> for RelPath { + fn as_ref(&self) -> &Utf8Path { + &self.0 + } +} impl AsRef<Path> for RelPath { fn as_ref(&self) -> &Path { - &self.0 + self.0.as_ref() } } impl RelPath { /// Creates a new `RelPath` from `path`, without checking if it is relative. - pub fn new_unchecked(path: &Path) -> &RelPath { - unsafe { &*(path as *const Path as *const RelPath) } + pub fn new_unchecked(path: &Utf8Path) -> &RelPath { + unsafe { &*(path as *const Utf8Path as *const RelPath) } } - /// Equivalent of [`Path::to_path_buf`] for `RelPath`. + /// Equivalent of [`Utf8Path::to_path_buf`] for `RelPath`. pub fn to_path_buf(&self) -> RelPathBuf { RelPathBuf::try_from(self.0.to_path_buf()).unwrap() } + + pub fn as_utf8_path(&self) -> &Utf8Path { + self.as_ref() + } + + pub fn as_str(&self) -> &str { + self.0.as_str() + } } /// Taken from <https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85> -fn normalize_path(path: &Path) -> PathBuf { +fn normalize_path(path: &Utf8Path) -> Utf8PathBuf { let mut components = path.components().peekable(); - let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().copied() { + let mut ret = if let Some(c @ Utf8Component::Prefix(..)) = components.peek().copied() { components.next(); - PathBuf::from(c.as_os_str()) + Utf8PathBuf::from(c.as_str()) } else { - PathBuf::new() + Utf8PathBuf::new() }; for component in components { match component { - Component::Prefix(..) => unreachable!(), - Component::RootDir => { - ret.push(component.as_os_str()); + Utf8Component::Prefix(..) => unreachable!(), + Utf8Component::RootDir => { + ret.push(component.as_str()); } - Component::CurDir => {} - Component::ParentDir => { + Utf8Component::CurDir => {} + Utf8Component::ParentDir => { ret.pop(); } - Component::Normal(c) => { + Utf8Component::Normal(c) => { ret.push(c); } } |