Enable the use of multiple dots in FS Shell commands (#1547)

Every dot after `..` means another parent directory.
This commit is contained in:
Kevin Del Castillo
2020-04-06 06:28:56 -05:00
committed by GitHub
parent c4daa2e40f
commit d0618b0b32
7 changed files with 256 additions and 56 deletions

View File

@ -76,29 +76,6 @@ impl FilesystemShell {
hinter: HistoryHinter {},
}
}
fn canonicalize(&self, path: impl AsRef<Path>) -> std::io::Result<PathBuf> {
let path = if path.as_ref().is_relative() {
let components = path.as_ref().components();
let mut result = PathBuf::from(self.path());
for component in components {
match component {
Component::CurDir => { /* ignore current dir */ }
Component::ParentDir => {
result.pop();
}
Component::Normal(normal) => result.push(normal),
_ => {}
}
}
result
} else {
path.as_ref().into()
};
dunce::canonicalize(path)
}
}
impl Shell for FilesystemShell {
@ -127,7 +104,7 @@ impl Shell for FilesystemShell {
let (path, p_tag) = match path {
Some(p) => {
let p_tag = p.tag;
let mut p = p.item;
let mut p = normalize(p.item);
if p.is_dir() {
if is_empty_dir(&p) {
return Ok(OutputStream::empty());
@ -210,7 +187,7 @@ impl Shell for FilesystemShell {
if target == Path::new("-") {
PathBuf::from(&self.last_path)
} else {
let path = self.canonicalize(target).map_err(|_| {
let path = canonicalize(self.path(), target).map_err(|_| {
ShellError::labeled_error(
"Cannot change to directory",
"directory not found",
@ -276,11 +253,9 @@ impl Shell for FilesystemShell {
) -> Result<OutputStream, ShellError> {
let name_tag = name;
let mut source = PathBuf::from(path);
let mut destination = PathBuf::from(path);
source.push(&src.item);
destination.push(&dst.item);
let path = Path::new(path);
let source = normalize(path.join(&src.item));
let mut destination = normalize(path.join(&dst.item));
let sources: Vec<_> = match glob::glob(&source.to_string_lossy()) {
Ok(files) => files.collect(),
@ -548,7 +523,7 @@ impl Shell for FilesystemShell {
name: Tag,
path: &str,
) -> Result<OutputStream, ShellError> {
let full_path = PathBuf::from(path);
let path = Path::new(path);
if directories.is_empty() {
return Err(ShellError::labeled_error(
@ -559,11 +534,7 @@ impl Shell for FilesystemShell {
}
for dir in directories.iter() {
let create_at = {
let mut loc = full_path.clone();
loc.push(&dir.item);
loc
};
let create_at = normalize(path.join(&dir.item));
let dir_res = std::fs::create_dir_all(create_at);
if let Err(reason) = dir_res {
@ -586,11 +557,9 @@ impl Shell for FilesystemShell {
) -> Result<OutputStream, ShellError> {
let name_tag = name;
let mut source = PathBuf::from(path);
let mut destination = PathBuf::from(path);
source.push(&src.item);
destination.push(&dst.item);
let path = Path::new(path);
let source = normalize(path.join(&src.item));
let mut destination = normalize(path.join(&dst.item));
let sources: Vec<_> = match glob::glob(&source.to_string_lossy()) {
Ok(files) => files.collect(),
@ -605,11 +574,12 @@ impl Shell for FilesystemShell {
if sources.is_empty() {
return Err(ShellError::labeled_error(
"Invalid File or Pattern.",
"Invalid File or Pattern",
"Invalid file or pattern.",
"invalid file or pattern",
src.tag,
));
}
let destination_file_name = {
match destination.file_name() {
Some(name) => PathBuf::from(name),
@ -991,6 +961,7 @@ impl Shell for FilesystemShell {
));
}
let path = Path::new(path);
let mut all_targets: HashMap<PathBuf, Tag> = HashMap::new();
for target in targets {
let all_dots = target
@ -1006,8 +977,7 @@ impl Shell for FilesystemShell {
));
}
let mut path = PathBuf::from(path);
path.push(&target.item);
let path = normalize(path.join(&target.item));
match glob::glob(&path.to_string_lossy()) {
Ok(files) => {
for file in files {
@ -1180,3 +1150,48 @@ fn is_hidden_dir(dir: impl AsRef<Path>) -> bool {
}
}
}
fn normalize(path: impl AsRef<Path>) -> PathBuf {
let mut normalized = PathBuf::new();
for component in path.as_ref().components() {
match component {
Component::Normal(normal) => {
if let Some(normal) = normal.to_str() {
if normal.chars().all(|c| c == '.') {
for _ in 0..(normal.len() - 1) {
normalized.push("..");
}
} else {
normalized.push(normal);
}
} else {
normalized.push(normal);
}
}
c => normalized.push(c.as_os_str()),
}
}
normalized
}
fn canonicalize(relative_to: impl AsRef<Path>, path: impl AsRef<Path>) -> std::io::Result<PathBuf> {
let path = if path.as_ref().is_relative() {
let mut result = relative_to.as_ref().to_path_buf();
normalize(path.as_ref())
.components()
.for_each(|component| match component {
Component::ParentDir => {
result.pop();
}
Component::Normal(normal) => result.push(normal),
_ => {}
});
result
} else {
path.as_ref().into()
};
dunce::canonicalize(path)
}