mirror of
https://github.com/nushell/nushell
synced 2024-12-27 05:23:11 +00:00
Refactor message printing in rm
(#12799)
# Description Changes the iterator in `rm` to be an iterator over `Result<Option<String>, ShellError>` (an optional message or error) instead of an iterator over `Value`. Then, the iterator is consumed and each message is printed. This allows the `PipelineData::print_not_formatted` method to be removed.
This commit is contained in:
parent
948b299e65
commit
3b3f48202c
2 changed files with 116 additions and 146 deletions
|
@ -3,7 +3,7 @@ use super::util::{get_rest_for_glob_pattern, try_interaction};
|
||||||
use nu_engine::{command_prelude::*, env::current_dir};
|
use nu_engine::{command_prelude::*, env::current_dir};
|
||||||
use nu_glob::MatchOptions;
|
use nu_glob::MatchOptions;
|
||||||
use nu_path::expand_path_with;
|
use nu_path::expand_path_with;
|
||||||
use nu_protocol::NuGlob;
|
use nu_protocol::{report_error_new, NuGlob};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::prelude::FileTypeExt;
|
use std::os::unix::prelude::FileTypeExt;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -118,8 +118,6 @@ fn rm(
|
||||||
let interactive = call.has_flag(engine_state, stack, "interactive")?;
|
let interactive = call.has_flag(engine_state, stack, "interactive")?;
|
||||||
let interactive_once = call.has_flag(engine_state, stack, "interactive-once")? && !interactive;
|
let interactive_once = call.has_flag(engine_state, stack, "interactive-once")? && !interactive;
|
||||||
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
|
||||||
|
|
||||||
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
||||||
|
|
||||||
if paths.is_empty() {
|
if paths.is_empty() {
|
||||||
|
@ -341,132 +339,130 @@ fn rm(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
all_targets
|
let iter = all_targets.into_iter().map(move |(f, span)| {
|
||||||
.into_iter()
|
let is_empty = || match f.read_dir() {
|
||||||
.map(move |(f, span)| {
|
Ok(mut p) => p.next().is_none(),
|
||||||
let is_empty = || match f.read_dir() {
|
Err(_) => false,
|
||||||
Ok(mut p) => p.next().is_none(),
|
};
|
||||||
Err(_) => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Ok(metadata) = f.symlink_metadata() {
|
if let Ok(metadata) = f.symlink_metadata() {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let is_socket = metadata.file_type().is_socket();
|
let is_socket = metadata.file_type().is_socket();
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let is_fifo = metadata.file_type().is_fifo();
|
let is_fifo = metadata.file_type().is_fifo();
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
let is_socket = false;
|
let is_socket = false;
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
let is_fifo = false;
|
let is_fifo = false;
|
||||||
|
|
||||||
if metadata.is_file()
|
if metadata.is_file()
|
||||||
|| metadata.file_type().is_symlink()
|
|| metadata.file_type().is_symlink()
|
||||||
|| recursive
|
|| recursive
|
||||||
|| is_socket
|
|| is_socket
|
||||||
|| is_fifo
|
|| is_fifo
|
||||||
|| is_empty()
|
|| is_empty()
|
||||||
{
|
{
|
||||||
let (interaction, confirmed) = try_interaction(
|
let (interaction, confirmed) = try_interaction(
|
||||||
interactive,
|
interactive,
|
||||||
format!("rm: remove '{}'? ", f.to_string_lossy()),
|
format!("rm: remove '{}'? ", f.to_string_lossy()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = if let Err(e) = interaction {
|
let result = if let Err(e) = interaction {
|
||||||
let e = Error::new(ErrorKind::Other, &*e.to_string());
|
Err(Error::new(ErrorKind::Other, &*e.to_string()))
|
||||||
Err(e)
|
} else if interactive && !confirmed {
|
||||||
} else if interactive && !confirmed {
|
Ok(())
|
||||||
Ok(())
|
} else if TRASH_SUPPORTED && (trash || (rm_always_trash && !permanent)) {
|
||||||
} else if TRASH_SUPPORTED && (trash || (rm_always_trash && !permanent)) {
|
#[cfg(all(
|
||||||
#[cfg(all(
|
feature = "trash-support",
|
||||||
feature = "trash-support",
|
not(any(target_os = "android", target_os = "ios"))
|
||||||
not(any(target_os = "android", target_os = "ios"))
|
))]
|
||||||
))]
|
{
|
||||||
{
|
trash::delete(&f).map_err(|e: trash::Error| {
|
||||||
trash::delete(&f).map_err(|e: trash::Error| {
|
Error::new(ErrorKind::Other, format!("{e:?}\nTry '--permanent' flag"))
|
||||||
Error::new(
|
})
|
||||||
ErrorKind::Other,
|
|
||||||
format!("{e:?}\nTry '--permanent' flag"),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should not be reachable since we error earlier if
|
|
||||||
// these options are given on an unsupported platform
|
|
||||||
#[cfg(any(
|
|
||||||
not(feature = "trash-support"),
|
|
||||||
target_os = "android",
|
|
||||||
target_os = "ios"
|
|
||||||
))]
|
|
||||||
{
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
} else if metadata.is_symlink() {
|
|
||||||
// In Windows, symlink pointing to a directory can be removed using
|
|
||||||
// std::fs::remove_dir instead of std::fs::remove_file.
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
f.metadata().and_then(|metadata| {
|
|
||||||
if metadata.is_dir() {
|
|
||||||
std::fs::remove_dir(&f)
|
|
||||||
} else {
|
|
||||||
std::fs::remove_file(&f)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
std::fs::remove_file(&f)
|
|
||||||
} else if metadata.is_file() || is_socket || is_fifo {
|
|
||||||
std::fs::remove_file(&f)
|
|
||||||
} else {
|
|
||||||
std::fs::remove_dir_all(&f)
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(e) = result {
|
|
||||||
let msg = format!("Could not delete {:}: {e:}", f.to_string_lossy());
|
|
||||||
Value::error(ShellError::RemoveNotPossible { msg, span }, span)
|
|
||||||
} else if verbose {
|
|
||||||
let msg = if interactive && !confirmed {
|
|
||||||
"not deleted"
|
|
||||||
} else {
|
|
||||||
"deleted"
|
|
||||||
};
|
|
||||||
let val = format!("{} {:}", msg, f.to_string_lossy());
|
|
||||||
Value::string(val, span)
|
|
||||||
} else {
|
|
||||||
Value::nothing(span)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Should not be reachable since we error earlier if
|
||||||
|
// these options are given on an unsupported platform
|
||||||
|
#[cfg(any(
|
||||||
|
not(feature = "trash-support"),
|
||||||
|
target_os = "android",
|
||||||
|
target_os = "ios"
|
||||||
|
))]
|
||||||
|
{
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
} else if metadata.is_symlink() {
|
||||||
|
// In Windows, symlink pointing to a directory can be removed using
|
||||||
|
// std::fs::remove_dir instead of std::fs::remove_file.
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
f.metadata().and_then(|metadata| {
|
||||||
|
if metadata.is_dir() {
|
||||||
|
std::fs::remove_dir(&f)
|
||||||
|
} else {
|
||||||
|
std::fs::remove_file(&f)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
std::fs::remove_file(&f)
|
||||||
|
} else if metadata.is_file() || is_socket || is_fifo {
|
||||||
|
std::fs::remove_file(&f)
|
||||||
} else {
|
} else {
|
||||||
let error = format!("Cannot remove {:}. try --recursive", f.to_string_lossy());
|
std::fs::remove_dir_all(&f)
|
||||||
Value::error(
|
};
|
||||||
ShellError::GenericError {
|
|
||||||
error,
|
if let Err(e) = result {
|
||||||
msg: "cannot remove non-empty directory".into(),
|
let msg = format!("Could not delete {:}: {e:}", f.to_string_lossy());
|
||||||
span: Some(span),
|
Err(ShellError::RemoveNotPossible { msg, span })
|
||||||
help: None,
|
} else if verbose {
|
||||||
inner: vec![],
|
let msg = if interactive && !confirmed {
|
||||||
},
|
"not deleted"
|
||||||
span,
|
} else {
|
||||||
)
|
"deleted"
|
||||||
|
};
|
||||||
|
Ok(Some(format!("{} {:}", msg, f.to_string_lossy())))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let error = format!("no such file or directory: {:}", f.to_string_lossy());
|
let error = format!("Cannot remove {:}. try --recursive", f.to_string_lossy());
|
||||||
Value::error(
|
Err(ShellError::GenericError {
|
||||||
ShellError::GenericError {
|
error,
|
||||||
error,
|
msg: "cannot remove non-empty directory".into(),
|
||||||
msg: "no such file or directory".into(),
|
span: Some(span),
|
||||||
span: Some(span),
|
help: None,
|
||||||
help: None,
|
inner: vec![],
|
||||||
inner: vec![],
|
})
|
||||||
},
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
} else {
|
||||||
.filter(|x| !matches!(x.get_type(), Type::Nothing))
|
let error = format!("no such file or directory: {:}", f.to_string_lossy());
|
||||||
.into_pipeline_data(span, ctrlc)
|
Err(ShellError::GenericError {
|
||||||
.print_not_formatted(engine_state, false, true)?;
|
error,
|
||||||
|
msg: "no such file or directory".into(),
|
||||||
|
span: Some(span),
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for result in iter {
|
||||||
|
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
|
||||||
|
return Err(ShellError::InterruptedByUser {
|
||||||
|
span: Some(call.head),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(None) => {}
|
||||||
|
Ok(Some(msg)) => eprintln!("{msg}"),
|
||||||
|
Err(err) => report_error_new(engine_state, &err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
}
|
}
|
||||||
|
|
|
@ -877,32 +877,6 @@ impl PipelineData {
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume and print self data immediately.
|
|
||||||
///
|
|
||||||
/// Unlike [`.print()`] does not call `table` to format data and just prints it
|
|
||||||
/// one element on a line
|
|
||||||
/// * `no_newline` controls if we need to attach newline character to output.
|
|
||||||
/// * `to_stderr` controls if data is output to stderr, when the value is false, the data is output to stdout.
|
|
||||||
pub fn print_not_formatted(
|
|
||||||
self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
no_newline: bool,
|
|
||||||
to_stderr: bool,
|
|
||||||
) -> Result<i64, ShellError> {
|
|
||||||
if let PipelineData::ExternalStream {
|
|
||||||
stdout: stream,
|
|
||||||
stderr: stderr_stream,
|
|
||||||
exit_code,
|
|
||||||
..
|
|
||||||
} = self
|
|
||||||
{
|
|
||||||
print_if_stream(stream, stderr_stream, to_stderr, exit_code)
|
|
||||||
} else {
|
|
||||||
let config = engine_state.get_config();
|
|
||||||
self.write_all_and_flush(engine_state, config, no_newline, to_stderr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_all_and_flush(
|
fn write_all_and_flush(
|
||||||
self,
|
self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
|
Loading…
Reference in a new issue