From a32cff333dd34b7db886317470f1301f0266b9e7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 14 Jun 2020 14:08:28 +0200 Subject: Introduce paths crate It's a good idea to distinguish between absolute and relative paths at the type level, to avoid accidental dependency on the cwd, which really shouldn't matter for rust-analyzer service --- crates/paths/src/lib.rs | 123 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 crates/paths/src/lib.rs (limited to 'crates/paths/src') diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs new file mode 100644 index 000000000..c7ce0c42f --- /dev/null +++ b/crates/paths/src/lib.rs @@ -0,0 +1,123 @@ +//! Thin wrappers around `std::path`, distinguishing between absolute and +//! relative paths. +use std::{ + convert::{TryFrom, TryInto}, + ops, + path::{Component, Path, PathBuf}, +}; + +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub struct AbsPathBuf(PathBuf); + +impl From for PathBuf { + fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf { + path_buf + } +} + +impl ops::Deref for AbsPathBuf { + type Target = AbsPath; + fn deref(&self) -> &AbsPath { + self.as_path() + } +} + +impl AsRef for AbsPathBuf { + fn as_ref(&self) -> &Path { + self.0.as_path() + } +} + +impl TryFrom for AbsPathBuf { + type Error = PathBuf; + fn try_from(path_buf: PathBuf) -> Result { + if !path_buf.is_absolute() { + return Err(path_buf); + } + Ok(AbsPathBuf(path_buf)) + } +} + +impl TryFrom<&str> for AbsPathBuf { + type Error = PathBuf; + fn try_from(path: &str) -> Result { + AbsPathBuf::try_from(PathBuf::from(path)) + } +} + +impl AbsPathBuf { + pub fn as_path(&self) -> &AbsPath { + AbsPath::new_unchecked(self.0.as_path()) + } + pub fn pop(&mut self) -> bool { + self.0.pop() + } +} + +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +#[repr(transparent)] +pub struct AbsPath(Path); + +impl ops::Deref for AbsPath { + type Target = Path; + fn deref(&self) -> &Path { + &self.0 + } +} + +impl AsRef for AbsPath { + fn as_ref(&self) -> &Path { + &self.0 + } +} + +impl<'a> TryFrom<&'a Path> for &'a AbsPath { + type Error = &'a Path; + fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> { + if !path.is_absolute() { + return Err(path); + } + Ok(AbsPath::new_unchecked(path)) + } +} + +impl AbsPath { + fn new_unchecked(path: &Path) -> &AbsPath { + unsafe { &*(path as *const Path as *const AbsPath) } + } + + pub fn join(&self, path: impl AsRef) -> AbsPathBuf { + self.as_ref().join(path).try_into().unwrap() + } + pub fn normalize(&self) -> AbsPathBuf { + AbsPathBuf(normalize_path(&self.0)) + } +} + +// https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 +fn normalize_path(path: &Path) -> PathBuf { + let mut components = path.components().peekable(); + let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { + components.next(); + PathBuf::from(c.as_os_str()) + } else { + PathBuf::new() + }; + + for component in components { + match component { + Component::Prefix(..) => unreachable!(), + Component::RootDir => { + ret.push(component.as_os_str()); + } + Component::CurDir => {} + Component::ParentDir => { + ret.pop(); + } + Component::Normal(c) => { + ret.push(c); + } + } + } + ret +} -- cgit v1.2.3