mirror of
https://github.com/nushell/nushell.git
synced 2024-11-25 09:53:43 +01:00
Have FilesystemShell#rm correctly delete symlinks (#1550)
This commit is contained in:
parent
6a604491f5
commit
0a198b9bd0
@ -16,7 +16,6 @@ use rustyline::completion::FilenameCompleter;
|
|||||||
use rustyline::hint::{Hinter, HistoryHinter};
|
use rustyline::hint::{Hinter, HistoryHinter};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
use trash as SendToTrash;
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
@ -995,10 +994,15 @@ impl Shell for FilesystemShell {
|
|||||||
|
|
||||||
let mut all_targets: HashMap<PathBuf, Tag> = HashMap::new();
|
let mut all_targets: HashMap<PathBuf, Tag> = HashMap::new();
|
||||||
for target in targets {
|
for target in targets {
|
||||||
if target.item.to_str() == Some(".") || target.item.to_str() == Some("..") {
|
let all_dots = target
|
||||||
|
.item
|
||||||
|
.to_str()
|
||||||
|
.map_or(false, |v| v.chars().all(|c| c == '.'));
|
||||||
|
|
||||||
|
if all_dots {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Remove aborted. \".\" or \"..\" may not be removed.",
|
"Cannot remove any parent directory",
|
||||||
"\".\" or \"..\" may not be removed",
|
"cannot remove any parent directory",
|
||||||
target.tag,
|
target.tag,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -1015,9 +1019,8 @@ impl Shell for FilesystemShell {
|
|||||||
.or_insert_with(|| target.tag.clone());
|
.or_insert_with(|| target.tag.clone());
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let msg = format!("Could not remove {:}", path.to_string_lossy());
|
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
msg,
|
format!("Could not remove {:}", path.to_string_lossy()),
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
&target.tag,
|
&target.tag,
|
||||||
));
|
));
|
||||||
@ -1027,7 +1030,7 @@ impl Shell for FilesystemShell {
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
format!("Remove aborted. {:}", e.to_string()),
|
e.to_string(),
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
&name_tag,
|
&name_tag,
|
||||||
))
|
))
|
||||||
@ -1036,91 +1039,63 @@ impl Shell for FilesystemShell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if all_targets.is_empty() {
|
if all_targets.is_empty() {
|
||||||
Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Remove aborted. No valid paths",
|
"No valid paths",
|
||||||
"no valid paths",
|
"no valid paths",
|
||||||
name_tag,
|
name_tag,
|
||||||
))
|
));
|
||||||
} else {
|
}
|
||||||
let stream = async_stream! {
|
|
||||||
for (f, tag) in all_targets.iter() {
|
|
||||||
let is_empty = match f.read_dir() {
|
|
||||||
Ok(mut p) => p.next().is_none(),
|
|
||||||
Err(_) => false
|
|
||||||
};
|
|
||||||
|
|
||||||
let valid_target =
|
let stream = async_stream! {
|
||||||
f.exists() && (!f.is_dir() || (is_empty || recursive.item));
|
for (f, tag) in all_targets.iter() {
|
||||||
if valid_target {
|
let is_empty = || match f.read_dir() {
|
||||||
if trash.item {
|
Ok(mut p) => p.next().is_none(),
|
||||||
match SendToTrash::remove(f) {
|
Err(_) => false
|
||||||
Err(e) => {
|
};
|
||||||
let msg = format!(
|
|
||||||
"Could not delete {:}",
|
if let Ok(metadata) = f.symlink_metadata() {
|
||||||
f.to_string_lossy()
|
if metadata.is_file() || metadata.file_type().is_symlink() || recursive.item || is_empty() {
|
||||||
);
|
let result = if trash.item {
|
||||||
let label = format!("{:?}", e);
|
trash::remove(f)
|
||||||
yield Err(ShellError::labeled_error(
|
.map_err(|e| f.to_string_lossy())
|
||||||
msg,
|
} else if metadata.is_file() {
|
||||||
label,
|
std::fs::remove_file(f)
|
||||||
tag,
|
.map_err(|e| f.to_string_lossy())
|
||||||
))
|
|
||||||
},
|
|
||||||
Ok(()) => {
|
|
||||||
let val = format!("deleted {:}", f.to_string_lossy()).into();
|
|
||||||
yield Ok(ReturnSuccess::Value(val))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let success = if f.is_dir() {
|
std::fs::remove_dir_all(f)
|
||||||
std::fs::remove_dir_all(f)
|
.map_err(|e| f.to_string_lossy())
|
||||||
} else {
|
};
|
||||||
std::fs::remove_file(f)
|
|
||||||
};
|
if let Err(e) = result {
|
||||||
match success {
|
let msg = format!("Could not delete {:}", e);
|
||||||
Err(e) => {
|
yield Err(ShellError::labeled_error(msg, e, tag))
|
||||||
let msg = format!(
|
} else {
|
||||||
"Could not delete {:}",
|
let val = format!("deleted {:}", f.to_string_lossy()).into();
|
||||||
f.to_string_lossy()
|
yield Ok(ReturnSuccess::Value(val))
|
||||||
);
|
|
||||||
yield Err(ShellError::labeled_error(
|
|
||||||
msg,
|
|
||||||
e.to_string(),
|
|
||||||
tag,
|
|
||||||
))
|
|
||||||
},
|
|
||||||
Ok(()) => {
|
|
||||||
let val = format!("deleted {:}", f.to_string_lossy()).into();
|
|
||||||
yield Ok(ReturnSuccess::Value(
|
|
||||||
val,
|
|
||||||
))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if f.is_dir() {
|
let msg = format!(
|
||||||
let msg = format!(
|
"Cannot remove {:}. try --recursive",
|
||||||
"Cannot remove {:}. try --recursive",
|
f.to_string_lossy()
|
||||||
f.to_string_lossy()
|
);
|
||||||
);
|
yield Err(ShellError::labeled_error(
|
||||||
yield Err(ShellError::labeled_error(
|
msg,
|
||||||
msg,
|
"cannot remove non-empty directory",
|
||||||
"cannot remove non-empty directory",
|
tag,
|
||||||
tag,
|
))
|
||||||
))
|
|
||||||
} else {
|
|
||||||
let msg = format!("Invalid file: {:}", f.to_string_lossy());
|
|
||||||
yield Err(ShellError::labeled_error(
|
|
||||||
msg,
|
|
||||||
"invalid file",
|
|
||||||
tag,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let msg = format!("no such file or directory: {:}", f.to_string_lossy());
|
||||||
|
yield Err(ShellError::labeled_error(
|
||||||
|
msg,
|
||||||
|
"no such file or directory",
|
||||||
|
tag,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
Ok(stream.to_output_stream())
|
};
|
||||||
}
|
|
||||||
|
Ok(stream.to_output_stream())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path(&self) -> String {
|
fn path(&self) -> String {
|
||||||
|
@ -144,7 +144,7 @@ fn errors_if_attempting_to_delete_single_dot_as_argument() {
|
|||||||
"rm ."
|
"rm ."
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(actual.contains("may not be removed"));
|
assert!(actual.contains("cannot remove any parent directory"));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ fn errors_if_attempting_to_delete_two_dot_as_argument() {
|
|||||||
"rm .."
|
"rm .."
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(actual.contains("may not be removed"));
|
assert!(actual.contains("cannot remove any parent directory"));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user