diff --git a/crates/nu-command/src/filesystem/rm.rs b/crates/nu-command/src/filesystem/rm.rs index 9b9e88b5ff..9696ae0c2f 100644 --- a/crates/nu-command/src/filesystem/rm.rs +++ b/crates/nu-command/src/filesystem/rm.rs @@ -3,7 +3,7 @@ use super::util::{get_rest_for_glob_pattern, try_interaction}; use nu_engine::{command_prelude::*, env::current_dir}; use nu_glob::MatchOptions; use nu_path::expand_path_with; -use nu_protocol::NuGlob; +use nu_protocol::{report_error_new, NuGlob}; #[cfg(unix)] use std::os::unix::prelude::FileTypeExt; use std::{ @@ -118,8 +118,6 @@ fn rm( let interactive = call.has_flag(engine_state, stack, "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)?; if paths.is_empty() { @@ -341,132 +339,130 @@ fn rm( } } - all_targets - .into_iter() - .map(move |(f, span)| { - let is_empty = || match f.read_dir() { - Ok(mut p) => p.next().is_none(), - Err(_) => false, - }; + let iter = all_targets.into_iter().map(move |(f, span)| { + let is_empty = || match f.read_dir() { + Ok(mut p) => p.next().is_none(), + Err(_) => false, + }; - if let Ok(metadata) = f.symlink_metadata() { - #[cfg(unix)] - let is_socket = metadata.file_type().is_socket(); - #[cfg(unix)] - let is_fifo = metadata.file_type().is_fifo(); + if let Ok(metadata) = f.symlink_metadata() { + #[cfg(unix)] + let is_socket = metadata.file_type().is_socket(); + #[cfg(unix)] + let is_fifo = metadata.file_type().is_fifo(); - #[cfg(not(unix))] - let is_socket = false; - #[cfg(not(unix))] - let is_fifo = false; + #[cfg(not(unix))] + let is_socket = false; + #[cfg(not(unix))] + let is_fifo = false; - if metadata.is_file() - || metadata.file_type().is_symlink() - || recursive - || is_socket - || is_fifo - || is_empty() - { - let (interaction, confirmed) = try_interaction( - interactive, - format!("rm: remove '{}'? ", f.to_string_lossy()), - ); + if metadata.is_file() + || metadata.file_type().is_symlink() + || recursive + || is_socket + || is_fifo + || is_empty() + { + let (interaction, confirmed) = try_interaction( + interactive, + format!("rm: remove '{}'? ", f.to_string_lossy()), + ); - let result = if let Err(e) = interaction { - let e = Error::new(ErrorKind::Other, &*e.to_string()); - Err(e) - } else if interactive && !confirmed { - Ok(()) - } else if TRASH_SUPPORTED && (trash || (rm_always_trash && !permanent)) { - #[cfg(all( - feature = "trash-support", - not(any(target_os = "android", target_os = "ios")) - ))] - { - trash::delete(&f).map_err(|e: trash::Error| { - 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) + let result = if let Err(e) = interaction { + Err(Error::new(ErrorKind::Other, &*e.to_string())) + } else if interactive && !confirmed { + Ok(()) + } else if TRASH_SUPPORTED && (trash || (rm_always_trash && !permanent)) { + #[cfg(all( + feature = "trash-support", + not(any(target_os = "android", target_os = "ios")) + ))] + { + trash::delete(&f).map_err(|e: trash::Error| { + 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 { - let error = format!("Cannot remove {:}. try --recursive", f.to_string_lossy()); - Value::error( - ShellError::GenericError { - error, - msg: "cannot remove non-empty directory".into(), - span: Some(span), - help: None, - inner: vec![], - }, - span, - ) + std::fs::remove_dir_all(&f) + }; + + if let Err(e) = result { + let msg = format!("Could not delete {:}: {e:}", f.to_string_lossy()); + Err(ShellError::RemoveNotPossible { msg, span }) + } else if verbose { + let msg = if interactive && !confirmed { + "not deleted" + } else { + "deleted" + }; + Ok(Some(format!("{} {:}", msg, f.to_string_lossy()))) + } else { + Ok(None) } } else { - let error = format!("no such file or directory: {:}", f.to_string_lossy()); - Value::error( - ShellError::GenericError { - error, - msg: "no such file or directory".into(), - span: Some(span), - help: None, - inner: vec![], - }, - span, - ) + let error = format!("Cannot remove {:}. try --recursive", f.to_string_lossy()); + Err(ShellError::GenericError { + error, + msg: "cannot remove non-empty directory".into(), + span: Some(span), + help: None, + inner: vec![], + }) } - }) - .filter(|x| !matches!(x.get_type(), Type::Nothing)) - .into_pipeline_data(span, ctrlc) - .print_not_formatted(engine_state, false, true)?; + } else { + let error = format!("no such file or directory: {:}", f.to_string_lossy()); + Err(ShellError::GenericError { + 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()) } diff --git a/crates/nu-protocol/src/pipeline_data/mod.rs b/crates/nu-protocol/src/pipeline_data/mod.rs index 5b36cf871f..297eb19c55 100644 --- a/crates/nu-protocol/src/pipeline_data/mod.rs +++ b/crates/nu-protocol/src/pipeline_data/mod.rs @@ -877,32 +877,6 @@ impl PipelineData { 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 { - 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( self, engine_state: &EngineState,