Skip to content

Commit

Permalink
Stripped down unix path (vercel/turborepo#4767)
Browse files Browse the repository at this point in the history
### Description

- Experiment with a very stripped down `RelativeUnixPath` implementation
 - Test out using it in `git.rs`

### Testing Instructions

Existing tests in `git.rs`
  • Loading branch information
Greg Soltis authored May 4, 2023
1 parent 57d561a commit d446153
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 77 deletions.
40 changes: 28 additions & 12 deletions crates/turbopath/src/absolute_system_path_buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use serde::Serialize;

use crate::{
AnchoredSystemPathBuf, IntoSystem, PathError, PathValidationError, RelativeSystemPathBuf,
RelativeUnixPath,
};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize)]
Expand Down Expand Up @@ -51,10 +52,10 @@ impl AbsoluteSystemPathBuf {
/// #[cfg(not(windows))]
/// assert_eq!(absolute_path.as_path(), Path::new("/Users/user"));
/// ```
pub fn new(unchecked_path: impl Into<PathBuf>) -> Result<Self, PathValidationError> {
pub fn new(unchecked_path: impl Into<PathBuf>) -> Result<Self, PathError> {
let unchecked_path = unchecked_path.into();
if !unchecked_path.is_absolute() {
return Err(PathValidationError::NotAbsolute(unchecked_path));
return Err(PathValidationError::NotAbsolute(unchecked_path).into());
}

let system_path = unchecked_path.into_system()?;
Expand Down Expand Up @@ -90,10 +91,7 @@ impl AbsoluteSystemPathBuf {
/// assert_eq!(anchored_path.as_path(), Path::new("Documents"));
/// }
/// ```
pub fn anchor(
&self,
path: &AbsoluteSystemPathBuf,
) -> Result<AnchoredSystemPathBuf, PathValidationError> {
pub fn anchor(&self, path: &AbsoluteSystemPathBuf) -> Result<AnchoredSystemPathBuf, PathError> {
AnchoredSystemPathBuf::new(self, path)
}

Expand Down Expand Up @@ -127,6 +125,14 @@ impl AbsoluteSystemPathBuf {
AbsoluteSystemPathBuf(self.0.join(path.as_path()))
}

pub fn join_unix_path(
&self,
unix_path: &RelativeUnixPath,
) -> Result<AbsoluteSystemPathBuf, PathError> {
let tail = unix_path.to_system_path()?;
Ok(AbsoluteSystemPathBuf(self.0.join(tail.as_path())))
}

pub fn as_path(&self) -> &Path {
self.0.as_path()
}
Expand Down Expand Up @@ -263,20 +269,24 @@ impl AsRef<Path> for AbsoluteSystemPathBuf {
mod tests {
use std::assert_matches::assert_matches;

use crate::{AbsoluteSystemPathBuf, PathValidationError};
use crate::{AbsoluteSystemPathBuf, PathError, PathValidationError};

#[cfg(not(windows))]
#[test]
fn test_absolute_system_path_buf_on_unix() {
assert!(AbsoluteSystemPathBuf::new("/Users/user").is_ok());
assert_matches!(
AbsoluteSystemPathBuf::new("./Users/user/"),
Err(PathValidationError::NotAbsolute(_))
Err(PathError::PathValidationError(
PathValidationError::NotAbsolute(_)
))
);

assert_matches!(
AbsoluteSystemPathBuf::new("Users"),
Err(PathValidationError::NotAbsolute(_))
Err(PathError::PathValidationError(
PathValidationError::NotAbsolute(_)
))
);
}

Expand All @@ -286,15 +296,21 @@ mod tests {
assert!(AbsoluteSystemPathBuf::new("C:\\Users\\user").is_ok());
assert_matches!(
AbsoluteSystemPathBuf::new(".\\Users\\user\\"),
Err(PathValidationError::NotAbsolute(_))
Err(PathError::PathValidationError(
PathValidationError::NotAbsolute(_)
))
);
assert_matches!(
AbsoluteSystemPathBuf::new("Users"),
Err(PathValidationError::NotAbsolute(_))
Err(PathError::PathValidationError(
PathValidationError::NotAbsolute(_)
))
);
assert_matches!(
AbsoluteSystemPathBuf::new("/Users/home"),
Err(PathValidationError::NotAbsolute(_))
Err(PathError::PathValidationError(
PathValidationError::NotAbsolute(_)
))
)
}
}
12 changes: 6 additions & 6 deletions crates/turbopath/src/anchored_system_path_buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ use std::path::{Path, PathBuf};

use serde::{Deserialize, Serialize};

use crate::{AbsoluteSystemPathBuf, IntoSystem, PathValidationError};
use crate::{AbsoluteSystemPathBuf, IntoSystem, PathError, PathValidationError};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
pub struct AnchoredSystemPathBuf(PathBuf);

impl TryFrom<&Path> for AnchoredSystemPathBuf {
type Error = PathValidationError;
type Error = PathError;

fn try_from(path: &Path) -> Result<Self, Self::Error> {
if path.is_absolute() {
return Err(PathValidationError::NotRelative(path.to_path_buf()));
return Err(PathValidationError::NotRelative(path.to_path_buf()).into());
}

Ok(AnchoredSystemPathBuf(path.into_system()?))
Expand All @@ -23,7 +23,7 @@ impl AnchoredSystemPathBuf {
pub fn new(
root: &AbsoluteSystemPathBuf,
path: &AbsoluteSystemPathBuf,
) -> Result<Self, PathValidationError> {
) -> Result<Self, PathError> {
let stripped_path = path
.as_path()
.strip_prefix(root.as_path())
Expand All @@ -37,10 +37,10 @@ impl AnchoredSystemPathBuf {
self.0.as_path()
}

pub fn to_str(&self) -> Result<&str, PathValidationError> {
pub fn to_str(&self) -> Result<&str, PathError> {
self.0
.to_str()
.ok_or_else(|| PathValidationError::InvalidUnicode(self.0.clone()))
.ok_or_else(|| PathValidationError::InvalidUnicode(self.0.clone()).into())
}
}

Expand Down
4 changes: 4 additions & 0 deletions crates/turbopath/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
mod absolute_system_path_buf;
mod anchored_system_path_buf;
mod relative_system_path_buf;
mod relative_unix_path;
mod relative_unix_path_buf;

use std::{
Expand All @@ -14,6 +15,7 @@ pub use absolute_system_path_buf::AbsoluteSystemPathBuf;
pub use anchored_system_path_buf::AnchoredSystemPathBuf;
use path_slash::{PathBufExt, PathExt};
pub use relative_system_path_buf::RelativeSystemPathBuf;
pub use relative_unix_path::RelativeUnixPath;
pub use relative_unix_path_buf::RelativeUnixPathBuf;

#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -44,6 +46,8 @@ pub enum PathValidationError {
NotRelative(PathBuf),
#[error("Path {0} is not parent of {1}")]
NotParent(String, String),
#[error("Path {0} is not a unix path")]
NotUnix(String),
}

trait IntoSystem {
Expand Down
4 changes: 4 additions & 0 deletions crates/turbopath/src/relative_system_path_buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ impl RelativeSystemPathBuf {
Ok(RelativeSystemPathBuf(system_path))
}

pub(crate) fn new_unchecked(unchecked_path: impl Into<PathBuf>) -> Self {
Self(unchecked_path.into())
}

pub fn as_path(&self) -> &Path {
&self.0
}
Expand Down
25 changes: 25 additions & 0 deletions crates/turbopath/src/relative_unix_path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use std::path::Path;

use crate::{IntoSystem, PathError, PathValidationError, RelativeSystemPathBuf};

pub struct RelativeUnixPath {
inner: Path,
}

impl RelativeUnixPath {
pub fn new<P: AsRef<Path>>(value: &P) -> Result<&Self, PathError> {
let path = value.as_ref();
if path.is_absolute() {
return Err(PathValidationError::NotRelative(path.to_owned()).into());
}
// copied from stdlib path.rs: relies on the representation of
// RelativeUnixPath being just a Path, the same way Path relies on
// just being an OsStr
Ok(unsafe { &*(path as *const Path as *const Self) })
}

pub fn to_system_path(&self) -> Result<RelativeSystemPathBuf, PathError> {
let system_path = self.inner.into_system()?;
Ok(RelativeSystemPathBuf::new_unchecked(system_path))
}
}
63 changes: 4 additions & 59 deletions crates/turbopath/src/relative_unix_path_buf.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use std::{
ffi::OsStr,
path::{Components, Path, PathBuf},
};
use std::path::PathBuf;

use serde::Serialize;

Expand Down Expand Up @@ -36,81 +33,29 @@ impl RelativeUnixPathBuf {
Ok(RelativeUnixPathBuf(path.into_unix()?))
}

pub fn as_path(&self) -> &Path {
&self.0
}

pub fn components(&self) -> Components<'_> {
self.0.components()
}

pub fn parent(&self) -> Option<Self> {
self.0
.parent()
.map(|p| RelativeUnixPathBuf(p.to_path_buf()))
}

pub fn starts_with<P: AsRef<Path>>(&self, base: P) -> bool {
self.0.starts_with(base.as_ref())
}

pub fn ends_with<P: AsRef<Path>>(&self, child: P) -> bool {
self.0.ends_with(child.as_ref())
}

pub fn join<P: AsRef<Path>>(&self, path: P) -> RelativeUnixPathBuf {
RelativeUnixPathBuf(self.0.join(path))
}

pub fn to_str(&self) -> Result<&str, PathValidationError> {
self.0
.to_str()
.ok_or_else(|| PathValidationError::InvalidUnicode(self.0.clone()))
}

pub fn file_name(&self) -> Option<&OsStr> {
self.0.file_name()
}

pub fn extension(&self) -> Option<&OsStr> {
self.0.extension()
}

pub fn into_path_buf(self) -> PathBuf {
self.0
}
}

#[cfg(test)]
mod tests {
use std::path::Path;

use super::*;

#[test]
fn test_relative_unix_path_buf() {
let path = RelativeUnixPathBuf::new(PathBuf::from("foo/bar")).unwrap();
assert_eq!(path.as_path(), Path::new("foo/bar"));
assert_eq!(path.components().count(), 2);
assert_eq!(path.parent().unwrap().as_path(), Path::new("foo"));
assert!(path.starts_with("foo"));
assert!(path.ends_with("bar"));
assert_eq!(path.join("baz").as_path(), Path::new("foo/bar/baz"));
assert_eq!(path.to_str().unwrap(), "foo/bar");
assert_eq!(path.file_name(), Some(OsStr::new("bar")));
assert_eq!(path.extension(), None);
}

#[test]
fn test_relative_unix_path_buf_with_extension() {
let path = RelativeUnixPathBuf::new(PathBuf::from("foo/bar.txt")).unwrap();
assert_eq!(path.as_path(), Path::new("foo/bar.txt"));
assert_eq!(path.components().count(), 2);
assert_eq!(path.parent().unwrap().as_path(), Path::new("foo"));
assert!(path.starts_with("foo"));
assert!(path.ends_with("bar.txt"));
assert_eq!(path.join("baz").as_path(), Path::new("foo/bar.txt/baz"));
assert_eq!(path.to_str().unwrap(), "foo/bar.txt");
assert_eq!(path.file_name(), Some(OsStr::new("bar.txt")));
assert_eq!(path.extension(), Some(OsStr::new("txt")));
}

#[test]
Expand All @@ -125,6 +70,6 @@ mod tests {
#[test]
fn test_convert_from_windows_path() {
let path = RelativeUnixPathBuf::new(PathBuf::from("foo\\bar")).unwrap();
assert_eq!(path.as_path(), Path::new("foo/bar"));
assert_eq!(path.0.as_path(), Path::new("foo/bar"));
}
}

0 comments on commit d446153

Please sign in to comment.