Skip to content

Commit

Permalink
Adding a proper AbsoluteSystemPath
Browse files Browse the repository at this point in the history
  • Loading branch information
NicholasLYang committed May 5, 2023
1 parent ff4ae8f commit 2093c1f
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 86 deletions.
109 changes: 109 additions & 0 deletions crates/turbopath/src/absolute_system_path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#[cfg(not(windows))]
use std::os::unix::fs::symlink as symlink_file;
#[cfg(windows)]
use std::os::windows::fs::symlink_file;
use std::{borrow::Cow, fmt, fs, fs::Metadata, io, path::Path};

use path_slash::PathExt;

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

pub struct AbsoluteSystemPath(Path);

impl ToOwned for AbsoluteSystemPath {
type Owned = AbsoluteSystemPathBuf;

fn to_owned(&self) -> Self::Owned {
AbsoluteSystemPathBuf(self.0.to_owned())
}
}

impl fmt::Display for AbsoluteSystemPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.display().fmt(f)
}
}

impl AsRef<Path> for AbsoluteSystemPath {
fn as_ref(&self) -> &Path {
&self.0
}
}

impl AbsoluteSystemPath {
pub fn new<P: AsRef<Path>>(value: &P) -> Result<Cow<Self>, PathError> {
let path = value.as_ref();
if path.is_relative() {
return Err(PathValidationError::NotAbsolute(path.to_owned()).into());
}

let system_path = path.to_slash().ok_or_else(|| {
PathError::PathValidationError(PathValidationError::InvalidUnicode(path.to_owned()))
})?;

// copied from stdlib path.rs: relies on the representation of
// AbsoluteSystemPath being just a Path, the same way Path relies on
// just being an OsStr
match system_path {
Cow::Owned(path) => Ok(Cow::Owned(AbsoluteSystemPathBuf::new(path)?)),
Cow::Borrowed(path) => {
let path = Path::new(path);
let absolute_system_path = unsafe { &*(path as *const Path as *const Self) };
Ok(Cow::Borrowed(absolute_system_path))
}
}
}

pub fn symlink_metadata(&self) -> Result<Metadata, PathError> {
Ok(fs::symlink_metadata(&self.0)?)
}

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

pub fn read_link(&self) -> Result<AbsoluteSystemPathBuf, PathError> {
let path = fs::read_link(&self.0)?;
Ok(AbsoluteSystemPathBuf::new(path)?)
}

pub fn join_relative(&self, path: &RelativeSystemPathBuf) -> AbsoluteSystemPathBuf {
let path = self.0.join(path.as_path());
AbsoluteSystemPathBuf(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 anchor(&self, path: &AbsoluteSystemPath) -> Result<AnchoredSystemPathBuf, PathError> {
AnchoredSystemPathBuf::new(self, path)
}

pub fn ensure_dir(&self) -> Result<(), io::Error> {
if let Some(parent) = self.0.parent() {
fs::create_dir_all(parent)
} else {
Ok(())
}
}

pub fn symlink_to_file<P: AsRef<Path>>(&self, to: P) -> Result<(), PathError> {
let system_path = to.as_ref();
let system_path = system_path.into_system()?;
symlink_file(&system_path, &self.0)?;
Ok(())
}

pub fn resolve(&self, path: &AnchoredSystemPathBuf) -> AbsoluteSystemPathBuf {
let path = self.0.join(path.as_path());
AbsoluteSystemPathBuf(path)
}
}
53 changes: 16 additions & 37 deletions crates/turbopath/src/absolute_system_path_buf.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
#[cfg(not(windows))]
use std::os::unix::fs::symlink as symlink_dir;
#[cfg(not(windows))]
use std::os::unix::fs::symlink as symlink_file;
#[cfg(windows)]
use std::os::windows::fs::{symlink_dir, symlink_file};
use std::{
borrow::Cow,
borrow::{Borrow, Cow},
ffi::OsStr,
fmt,
fs::{self, Metadata},
Expand All @@ -16,12 +10,19 @@ use std::{
use serde::Serialize;

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

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

impl Borrow<AbsoluteSystemPath> for AbsoluteSystemPathBuf {
fn borrow(&self) -> &AbsoluteSystemPath {
let path = self.as_path();
unsafe { &*(path as *const Path as *const AbsoluteSystemPath) }
}
}

impl AbsoluteSystemPathBuf {
/// Create a new AbsoluteSystemPathBuf from `unchecked_path`.
Expand Down Expand Up @@ -91,8 +92,8 @@ impl AbsoluteSystemPathBuf {
/// assert_eq!(anchored_path.as_path(), Path::new("Documents"));
/// }
/// ```
pub fn anchor(&self, path: &AbsoluteSystemPathBuf) -> Result<AnchoredSystemPathBuf, PathError> {
AnchoredSystemPathBuf::new(self, path)
pub fn anchor(&self, path: &AbsoluteSystemPath) -> Result<AnchoredSystemPathBuf, PathError> {
AnchoredSystemPathBuf::new(self.borrow(), path)
}

/// Resolves `path` with `self` as anchor.
Expand Down Expand Up @@ -125,14 +126,6 @@ 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 @@ -176,28 +169,14 @@ impl AbsoluteSystemPathBuf {
}

pub fn set_readonly(&self) -> Result<(), PathError> {
let mut perms = self.metadata()?.permissions();
let mut perms = self.symlink_metadata()?.permissions();
perms.set_readonly(true);
fs::set_permissions(self.0.as_path(), perms)?;
Ok(())
}

pub fn is_readonly(&self) -> Result<bool, PathError> {
Ok(self.metadata()?.permissions().readonly())
}

pub fn symlink_to_file<P: AsRef<Path>>(&self, to: P) -> Result<(), PathError> {
let system_path = to.as_ref();
let system_path = system_path.into_system()?;
symlink_file(&system_path, &self.0.as_path())?;
Ok(())
}

pub fn symlink_to_dir<P: AsRef<Path>>(&self, to: P) -> Result<(), PathError> {
let system_path = to.as_ref();
let system_path = system_path.into_system()?;
symlink_dir(&system_path, &self.0.as_path())?;
Ok(())
Ok(self.symlink_metadata()?.permissions().readonly())
}

pub fn read_symlink(&self) -> Result<PathBuf, io::Error> {
Expand Down Expand Up @@ -232,7 +211,7 @@ impl AbsoluteSystemPathBuf {
self.0.extension()
}

pub fn metadata(&self) -> Result<Metadata, PathError> {
pub fn symlink_metadata(&self) -> Result<Metadata, PathError> {
Ok(fs::symlink_metadata(&self.0)?)
}

Expand Down
7 changes: 2 additions & 5 deletions crates/turbopath/src/anchored_system_path_buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf};

use serde::{Deserialize, Serialize};

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

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
pub struct AnchoredSystemPathBuf(PathBuf);
Expand All @@ -20,10 +20,7 @@ impl TryFrom<&Path> for AnchoredSystemPathBuf {
}

impl AnchoredSystemPathBuf {
pub fn new(
root: &AbsoluteSystemPathBuf,
path: &AbsoluteSystemPathBuf,
) -> Result<Self, PathError> {
pub fn new(root: &AbsoluteSystemPath, path: &AbsoluteSystemPath) -> Result<Self, PathError> {
let stripped_path = path
.as_path()
.strip_prefix(root.as_path())
Expand Down
2 changes: 2 additions & 0 deletions crates/turbopath/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![feature(assert_matches)]

mod absolute_system_path;
mod absolute_system_path_buf;
mod anchored_system_path_buf;
mod relative_system_path_buf;
Expand All @@ -11,6 +12,7 @@ use std::{
path::{Path, PathBuf},
};

pub use absolute_system_path::AbsoluteSystemPath;
pub use absolute_system_path_buf::AbsoluteSystemPathBuf;
pub use anchored_system_path_buf::AnchoredSystemPathBuf;
use path_slash::{PathBufExt, PathExt};
Expand Down
4 changes: 2 additions & 2 deletions crates/turborepo-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! and in ffi.go before modifying this file.
mod lockfile;

use std::{mem::ManuallyDrop, path::PathBuf};
use std::{borrow::Borrow, mem::ManuallyDrop, path::PathBuf};

pub use lockfile::{patches, subgraph, transitive_closure};
use turbopath::AbsoluteSystemPathBuf;
Expand Down Expand Up @@ -155,7 +155,7 @@ pub extern "C" fn recursive_copy(buffer: Buffer) -> Buffer {
}
};

let response = match turborepo_fs::recursive_copy(&src, &dst) {
let response = match turborepo_fs::recursive_copy(src.borrow(), dst.borrow()) {
Ok(()) => proto::RecursiveCopyResponse { error: None },
Err(e) => proto::RecursiveCopyResponse {
error: Some(e.to_string()),
Expand Down
37 changes: 20 additions & 17 deletions crates/turborepo-fs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use std::fs::{self, DirBuilder, Metadata};
use std::{
borrow::Borrow,
fs::{self, DirBuilder, Metadata},
};

use anyhow::Result;
use turbopath::{AbsoluteSystemPathBuf, AnchoredSystemPathBuf};
use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf, AnchoredSystemPathBuf};
use walkdir::WalkDir;

pub fn recursive_copy(src: &AbsoluteSystemPathBuf, dst: &AbsoluteSystemPathBuf) -> Result<()> {
let src_metadata = src.metadata()?;
pub fn recursive_copy(src: &AbsoluteSystemPath, dst: &AbsoluteSystemPath) -> Result<()> {
let src_metadata = src.symlink_metadata()?;
if src_metadata.is_dir() {
let walker = WalkDir::new(src.as_path()).follow_links(false);
for entry in walker.into_iter() {
Expand Down Expand Up @@ -39,13 +42,13 @@ pub fn recursive_copy(src: &AbsoluteSystemPathBuf, dst: &AbsoluteSystemPathBuf)
false
};

let suffix = AnchoredSystemPathBuf::new(src, &path)?;
let suffix = AnchoredSystemPathBuf::new(src, path.borrow())?;
let target = dst.resolve(&suffix);
if is_dir_or_symlink_to_dir {
let src_metadata = entry.metadata()?;
make_dir_copy(&target, &src_metadata)?;
make_dir_copy(target.borrow(), &src_metadata)?;
} else {
copy_file_with_type(&path, file_type, &target)?;
copy_file_with_type(path.borrow(), file_type, target.borrow())?;
}
}
}
Expand All @@ -56,7 +59,7 @@ pub fn recursive_copy(src: &AbsoluteSystemPathBuf, dst: &AbsoluteSystemPathBuf)
}
}

fn make_dir_copy(dir: &AbsoluteSystemPathBuf, src_metadata: &Metadata) -> Result<()> {
fn make_dir_copy(dir: &AbsoluteSystemPath, src_metadata: &Metadata) -> Result<()> {
let mut builder = DirBuilder::new();
#[cfg(not(windows))]
{
Expand All @@ -68,21 +71,21 @@ fn make_dir_copy(dir: &AbsoluteSystemPathBuf, src_metadata: &Metadata) -> Result
Ok(())
}

pub fn copy_file(from: &AbsoluteSystemPathBuf, to: &AbsoluteSystemPathBuf) -> Result<()> {
let metadata = from.metadata()?;
pub fn copy_file(from: &AbsoluteSystemPath, to: &AbsoluteSystemPath) -> Result<()> {
let metadata = from.symlink_metadata()?;
copy_file_with_type(from, metadata.file_type(), to)
}

fn copy_file_with_type(
from: &AbsoluteSystemPathBuf,
from: &AbsoluteSystemPath,
from_type: fs::FileType,
to: &AbsoluteSystemPathBuf,
to: &AbsoluteSystemPath,
) -> Result<()> {
if from_type.is_symlink() {
let target = from.read_symlink()?;
let target = from.read_link()?;
to.ensure_dir()?;
if to.metadata().is_ok() {
to.remove()?;
if fs::symlink_metadata(to).is_ok() {
fs::remove_file(&to)?;
}
to.symlink_to_file(&target)?;
Ok(())
Expand Down Expand Up @@ -228,7 +231,7 @@ mod tests {
// This is very likely not ideal behavior, but leaving this test here to verify
// that it is what we expect at this point in time.
let dst_circle_path = dst_child_path.join_literal("circle");
let dst_circle_metadata = dst_circle_path.metadata()?;
let dst_circle_metadata = dst_circle_path.symlink_metadata()?;
assert_eq!(dst_circle_metadata.is_dir(), true);

let num_files = fs::read_dir(dst_circle_path.as_path())?.into_iter().count();
Expand All @@ -237,7 +240,7 @@ mod tests {
Ok(())
}

fn assert_file_matches(a: &AbsoluteSystemPathBuf, b: &AbsoluteSystemPathBuf) {
fn assert_file_matches(a: &AbsoluteSystemPath, b: &AbsoluteSystemPath) {
let a_contents = fs::read_to_string(a.as_path()).unwrap();
let b_contents = fs::read_to_string(b.as_path()).unwrap();
assert_eq!(a_contents, b_contents);
Expand Down
6 changes: 4 additions & 2 deletions crates/turborepo-lib/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::borrow::Borrow;

use anyhow::Result;
use sha2::{Digest, Sha256};
use tokio::sync::OnceCell;
Expand Down Expand Up @@ -48,7 +50,7 @@ impl CommandBase {
}

fn create_repo_config(&self) -> Result<()> {
let repo_config_path = get_repo_config_path(&self.repo_root);
let repo_config_path = get_repo_config_path(self.repo_root.borrow());

let repo_config = RepoConfigLoader::new(repo_config_path)
.with_api(self.args.api.clone())
Expand All @@ -66,7 +68,7 @@ impl CommandBase {
// currently do not have any commands that delete the repo config file
// and then attempt to read from it.
pub fn delete_repo_config_file(&mut self) -> Result<()> {
let repo_config_path = get_repo_config_path(&self.repo_root);
let repo_config_path = get_repo_config_path(self.repo_root.borrow());
if repo_config_path.exists() {
std::fs::remove_file(repo_config_path)?;
}
Expand Down

0 comments on commit 2093c1f

Please sign in to comment.