Split canonicalize function in two for missing and existing behavior (#1576)

* Split allow missing logic in two functions

* Replace use of old canonicalize
This commit is contained in:
Kevin Del Castillo 2020-04-12 03:33:38 -05:00 committed by GitHub
parent 57c62de66f
commit 38b2846024
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 55 additions and 50 deletions

View File

@ -25,13 +25,7 @@ pub fn normalize(path: impl AsRef<Path>) -> PathBuf {
normalized normalized
} }
pub struct AllowMissing(pub bool); fn canonicalize_core<P, Q>(relative_to: P, path: Q) -> PathBuf
pub fn canonicalize<P, Q>(
relative_to: P,
path: Q,
allow_missing: AllowMissing,
) -> io::Result<PathBuf>
where where
P: AsRef<Path>, P: AsRef<Path>,
Q: AsRef<Path>, Q: AsRef<Path>,
@ -57,7 +51,7 @@ where
(relative_to.as_ref().to_path_buf(), path) (relative_to.as_ref().to_path_buf(), path)
}; };
let path = if path.is_relative() { if path.is_relative() {
let mut result = relative_to; let mut result = relative_to;
path.components().for_each(|component| match component { path.components().for_each(|component| match component {
Component::ParentDir => { Component::ParentDir => {
@ -70,26 +64,44 @@ where
result result
} else { } else {
path path
}; }
}
let path = match std::fs::read_link(&path) { pub fn canonicalize_existing<P, Q>(relative_to: P, path: Q) -> io::Result<PathBuf>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let canonicalized = canonicalize_core(relative_to, path);
let path = match std::fs::read_link(&canonicalized) {
Ok(resolved) => resolved, Ok(resolved) => resolved,
Err(e) => { Err(e) => {
// We are here if path doesn't exist or isn't a symlink if canonicalized.exists() {
if allow_missing.0 || path.exists() { canonicalized
// Return if we allow missing paths or if the path
// actually exists, but wasn't a symlink
path
} else { } else {
return Err(e); return Err(e);
} }
} }
}; };
// De-UNC paths
Ok(dunce::simplified(&path).to_path_buf()) Ok(dunce::simplified(&path).to_path_buf())
} }
#[allow(dead_code)]
pub fn canonicalize_missing<P, Q>(relative_to: P, path: Q) -> PathBuf
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let canonicalized = canonicalize_core(relative_to, path);
let path = match std::fs::read_link(&canonicalized) {
Ok(resolved) => resolved,
Err(_) => canonicalized,
};
dunce::simplified(&path).to_path_buf()
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -106,62 +118,56 @@ mod tests {
} }
#[test] #[test]
fn canonicalize_two_dots_and_allow_missing() -> io::Result<()> { fn canonicalize_missing_two_dots() {
let relative_to = Path::new("/foo/bar"); // does not exists let relative_to = Path::new("/foo/bar");
let path = Path::new(".."); let path = Path::new("..");
assert_eq!( assert_eq!(
PathBuf::from("/foo"), PathBuf::from("/foo"), // missing path
canonicalize(relative_to, path, AllowMissing(true))? canonicalize_missing(relative_to, path)
); );
Ok(())
} }
#[test] #[test]
fn canonicalize_three_dots_and_allow_missing() -> io::Result<()> { fn canonicalize_missing_three_dots() {
let relative_to = Path::new("/foo/bar/baz"); // missing path let relative_to = Path::new("/foo/bar/baz");
let path = Path::new("..."); let path = Path::new("...");
assert_eq!( assert_eq!(
PathBuf::from("/foo"), PathBuf::from("/foo"), // missing path
canonicalize(relative_to, path, AllowMissing(true))? canonicalize_missing(relative_to, path)
); );
Ok(())
} }
#[test] #[test]
fn canonicalize_three_dots_with_redundant_dot_and_allow_missing() -> io::Result<()> { fn canonicalize_missing_three_dots_with_redundant_dot() {
let relative_to = Path::new("/foo/bar/baz"); // missing path let relative_to = Path::new("/foo/bar/baz");
let path = Path::new("./..."); let path = Path::new("./...");
assert_eq!( assert_eq!(
PathBuf::from("/foo"), PathBuf::from("/foo"), // missing path
canonicalize(relative_to, path, AllowMissing(true))? canonicalize_missing(relative_to, path)
); );
Ok(())
} }
#[test] #[test]
fn canonicalize_three_dots_and_disallow_missing() -> io::Result<()> { fn canonicalize_existing_three_dots() -> io::Result<()> {
let relative_to = Path::new("/foo/bar/"); // root is not missing let relative_to = Path::new("/foo/bar/");
let path = Path::new("..."); let path = Path::new("...");
assert_eq!( assert_eq!(
PathBuf::from("/"), PathBuf::from("/"), // existing path
canonicalize(relative_to, path, AllowMissing(false))? canonicalize_existing(relative_to, path)?
); );
Ok(()) Ok(())
} }
#[test] #[test]
fn canonicalize_three_dots_and_disallow_missing_should_fail() { fn canonicalize_existing_three_dots_should_fail() {
let relative_to = Path::new("/foo/bar/baz"); // foo is missing let relative_to = Path::new("/foo/bar/baz"); // '/foo' is missing
let path = Path::new("..."); let path = Path::new("...");
assert!(canonicalize(relative_to, path, AllowMissing(false)).is_err()); assert!(canonicalize_existing(relative_to, path).is_err());
} }
} }

View File

@ -5,7 +5,7 @@ use crate::commands::mkdir::MkdirArgs;
use crate::commands::mv::MoveArgs; use crate::commands::mv::MoveArgs;
use crate::commands::rm::RemoveArgs; use crate::commands::rm::RemoveArgs;
use crate::data::dir_entry_dict; use crate::data::dir_entry_dict;
use crate::path::{canonicalize, normalize, AllowMissing}; use crate::path::{canonicalize_existing, normalize};
use crate::prelude::*; use crate::prelude::*;
use crate::shell::completer::NuCompleter; use crate::shell::completer::NuCompleter;
use crate::shell::shell::Shell; use crate::shell::shell::Shell;
@ -188,14 +188,13 @@ impl Shell for FilesystemShell {
if target == Path::new("-") { if target == Path::new("-") {
PathBuf::from(&self.last_path) PathBuf::from(&self.last_path)
} else { } else {
let path = let path = canonicalize_existing(self.path(), target).map_err(|_| {
canonicalize(self.path(), target, AllowMissing(false)).map_err(|_| { ShellError::labeled_error(
ShellError::labeled_error( "Cannot change to directory",
"Cannot change to directory", "directory not found",
"directory not found", &v.tag,
&v.tag, )
) })?;
})?;
if !path.is_dir() { if !path.is_dir() {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(