Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid fs::canonicalize #539

Merged
merged 1 commit into from
Nov 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub(crate) use snafu::{ResultExt, Snafu};
pub(crate) use unicode_width::UnicodeWidthChar;

// modules
pub(crate) use crate::{config_error, keyword, search_error, setting};
pub(crate) use crate::{config_error, keyword, setting};

// functions
pub(crate) use crate::{default::default, empty::empty, load_dotenv::load_dotenv, output::output};
Expand Down
2 changes: 0 additions & 2 deletions src/config_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ pub(crate) enum ConfigError {
message
))]
Internal { message: String },
#[snafu(display("Could not canonicalize justfile path `{}`: {}", path.display(), source))]
JustfilePathCanonicalize { path: PathBuf, source: io::Error },
#[snafu(display("Failed to get current directory: {}", source))]
CurrentDir { source: io::Error },
#[snafu(display(
Expand Down
89 changes: 74 additions & 15 deletions src/search.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::common::*;

use std::path::Component;

const FILENAME: &str = "justfile";

pub(crate) struct Search {
Expand All @@ -25,12 +27,7 @@ impl Search {
}

SearchConfig::FromSearchDirectory { search_directory } => {
let search_directory =
search_directory
.canonicalize()
.context(search_error::Canonicalize {
path: search_directory,
})?;
let search_directory = Self::clean(invocation_directory, search_directory);

let justfile = Self::justfile(&search_directory)?;

Expand All @@ -43,7 +40,7 @@ impl Search {
}

SearchConfig::WithJustfile { justfile } => {
let justfile: PathBuf = justfile.to_path_buf();
let justfile = Self::clean(invocation_directory, justfile);

let working_directory = Self::working_directory_from_justfile(&justfile)?;

Expand All @@ -57,8 +54,8 @@ impl Search {
justfile,
working_directory,
} => Ok(Search {
justfile: justfile.to_path_buf(),
working_directory: working_directory.to_path_buf(),
justfile: Self::clean(invocation_directory, justfile),
working_directory: Self::clean(invocation_directory, working_directory),
}),
}
}
Expand Down Expand Up @@ -92,16 +89,30 @@ impl Search {
}
}

fn working_directory_from_justfile(justfile: &Path) -> SearchResult<PathBuf> {
let justfile_canonical = justfile
.canonicalize()
.context(search_error::Canonicalize { path: justfile })?;
fn clean(invocation_directory: &Path, path: &Path) -> PathBuf {
let path = invocation_directory.join(path);

let mut clean = Vec::new();

for component in path.components() {
if component == Component::ParentDir {
if let Some(Component::Normal(_)) = clean.last() {
clean.pop();
}
} else {
clean.push(component);
}
}

clean.into_iter().collect()
}

fn working_directory_from_justfile(justfile: &Path) -> SearchResult<PathBuf> {
Ok(
justfile_canonical
justfile
.parent()
.ok_or_else(|| SearchError::JustfileHadNoParent {
path: justfile_canonical.clone(),
path: justfile.to_path_buf(),
})?
.to_owned(),
)
Expand All @@ -111,6 +122,7 @@ impl Search {
#[cfg(test)]
mod tests {
use super::*;
use test_utilities::tmptree;

#[test]
fn not_found() {
Expand Down Expand Up @@ -228,4 +240,51 @@ mod tests {
_ => panic!("No errors were expected"),
}
}

#[test]
fn justfile_symlink_parent() {
let tmp = tmptree! {
src: "",
sub: {},
};

let src = tmp.path().join("src");
let sub = tmp.path().join("sub");
let justfile = sub.join("justfile");

#[cfg(unix)]
std::os::unix::fs::symlink(&src, &justfile).unwrap();

#[cfg(windows)]
std::os::windows::fs::symlink_file(&src, &justfile).unwrap();

let search_config = SearchConfig::FromInvocationDirectory;

let search = Search::search(&search_config, &sub).unwrap();

assert_eq!(search.justfile, justfile);
assert_eq!(search.working_directory, sub);
}

#[test]
fn clean() {
let cases = &[
("/", "foo", "/foo"),
("/bar", "/foo", "/foo"),
("//foo", "bar//baz", "/foo/bar/baz"),
("/", "..", "/"),
("/", "/..", "/"),
("/..", "", "/"),
("/../../../..", "../../../", "/"),
("/.", "./", "/"),
("/foo/../", "bar", "/bar"),
("/foo/bar", "..", "/foo"),
("/foo/bar/", "..", "/foo"),
];

for (prefix, suffix, want) in cases {
let have = Search::clean(Path::new(prefix), Path::new(suffix));
assert_eq!(have, Path::new(want));
}
}
}
13 changes: 3 additions & 10 deletions src/search_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ pub(crate) enum SearchError {
.map(|candidate| candidate.file_name().unwrap().to_string_lossy())
),
))]
MultipleCandidates {
candidates: Vec<PathBuf>,
},
MultipleCandidates { candidates: Vec<PathBuf> },
#[snafu(display(
"I/O error reading directory `{}`: {}",
directory.display(),
Expand All @@ -26,13 +24,8 @@ pub(crate) enum SearchError {
},
#[snafu(display("No justfile found"))]
NotFound,
Canonicalize {
path: PathBuf,
source: io::Error,
},
JustfileHadNoParent {
path: PathBuf,
},
#[snafu(display("Justfile path had no parent: {}", path.display()))]
JustfileHadNoParent { path: PathBuf },
}

impl Error for SearchError {}
Expand Down