From 521a1b2d8dd81779b3a87cc2bd772ec3e0d87044 Mon Sep 17 00:00:00 2001 From: mhead Date: Sun, 28 Jul 2024 23:43:22 +0530 Subject: [PATCH 1/9] uucore: add update control `none-fail` --- src/uu/cp/src/cp.rs | 3 +++ src/uu/mv/src/mv.rs | 16 ++++++++++++---- src/uucore/src/lib/features/update_control.rs | 4 +++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 90f4cb5383..cd95761a56 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1889,6 +1889,9 @@ fn handle_copy_mode( return Ok(()); } + update_control::UpdateMode::ReplaceNoneFail => { + return Err(Error::Error(format!("not replacing '{}'", dest.display()))); + } update_control::UpdateMode::ReplaceIfOlder => { let dest_metadata = fs::symlink_metadata(dest)?; diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index 7edecb960f..903bc52138 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -135,10 +135,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let backup_mode = backup_control::determine_backup_mode(&matches)?; let update_mode = update_control::determine_update_mode(&matches); - if overwrite_mode == OverwriteMode::NoClobber && backup_mode != BackupMode::NoBackup { + if backup_mode != BackupMode::NoBackup + && (overwrite_mode == OverwriteMode::NoClobber + || update_mode == UpdateMode::ReplaceNone + || update_mode == UpdateMode::ReplaceNoneFail) + { return Err(UUsageError::new( 1, - "options --backup and --no-clobber are mutually exclusive", + "cannot combine --backup with -n or --update=none-fail", )); } @@ -530,10 +534,14 @@ fn rename( return Ok(()); } + if opts.update == UpdateMode::ReplaceNoneFail { + let err_msg = format!("not replacing {}", to.quote()); + return Err(io::Error::new(io::ErrorKind::Other, err_msg)); + } + match opts.overwrite { OverwriteMode::NoClobber => { - let err_msg = format!("not replacing {}", to.quote()); - return Err(io::Error::new(io::ErrorKind::Other, err_msg)); + return Ok(()); } OverwriteMode::Interactive => { if !prompt_yes!("overwrite {}?", to.quote()) { diff --git a/src/uucore/src/lib/features/update_control.rs b/src/uucore/src/lib/features/update_control.rs index 72927c8d06..34cb8478bc 100644 --- a/src/uucore/src/lib/features/update_control.rs +++ b/src/uucore/src/lib/features/update_control.rs @@ -59,6 +59,7 @@ pub enum UpdateMode { /// --update=`older` /// -u ReplaceIfOlder, + ReplaceNoneFail, } pub mod arguments { @@ -76,7 +77,7 @@ pub mod arguments { clap::Arg::new(OPT_UPDATE) .long("update") .help("move only when the SOURCE file is newer than the destination file or when the destination file is missing") - .value_parser(ShortcutValueParser::new(["none", "all", "older"])) + .value_parser(ShortcutValueParser::new(["none", "all", "older","none-fail"])) .num_args(0..=1) .default_missing_value("older") .require_equals(true) @@ -130,6 +131,7 @@ pub fn determine_update_mode(matches: &ArgMatches) -> UpdateMode { "all" => UpdateMode::ReplaceAll, "none" => UpdateMode::ReplaceNone, "older" => UpdateMode::ReplaceIfOlder, + "none-fail" => UpdateMode::ReplaceNoneFail, _ => unreachable!("other args restricted by clap"), } } else if matches.get_flag(arguments::OPT_UPDATE_NO_ARG) { From 6b1036f70e4da61e5ca91ed8783bf9aacad65ec2 Mon Sep 17 00:00:00 2001 From: mhead Date: Sun, 28 Jul 2024 23:45:20 +0530 Subject: [PATCH 2/9] uucore: show suggestion when parse errors occurs because of an ambiguous value --- .../src/lib/parser/shortcut_value_parser.rs | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/uucore/src/lib/parser/shortcut_value_parser.rs b/src/uucore/src/lib/parser/shortcut_value_parser.rs index fe80bee3cd..4df2529815 100644 --- a/src/uucore/src/lib/parser/shortcut_value_parser.rs +++ b/src/uucore/src/lib/parser/shortcut_value_parser.rs @@ -2,7 +2,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore abcdefgh abef +// spell-checker:ignore abcdefgh abef Strs //! A parser that accepts shortcuts for values. //! `ShortcutValueParser` is similar to clap's `PossibleValuesParser` @@ -32,6 +32,7 @@ impl ShortcutValueParser { cmd: &clap::Command, arg: Option<&clap::Arg>, value: &str, + possible_values: &[&PossibleValue], ) -> clap::Error { let mut err = clap::Error::new(ErrorKind::InvalidValue).with_cmd(cmd); @@ -52,6 +53,26 @@ impl ShortcutValueParser { ContextValue::Strings(self.0.iter().map(|x| x.get_name().to_string()).collect()), ); + // if `possible_values` is not empty then that means this error is because of an ambiguous value. + if !possible_values.is_empty() { + let mut formatted_possible_values = String::new(); + for (i, s) in possible_values.iter().enumerate() { + formatted_possible_values.push_str(&format!("'{}'", s.get_name())); + if i < possible_values.len() - 2 { + formatted_possible_values.push_str(", "); + } else if i < possible_values.len() - 1 { + formatted_possible_values.push_str(" or "); + } + } + err.insert( + ContextKind::Suggested, + ContextValue::StyledStrs(vec![format!( + "It looks like '{}' could match several values. Did you mean {}?", + value, formatted_possible_values + ) + .into()]), + ); + } err } } @@ -76,13 +97,13 @@ impl TypedValueParser for ShortcutValueParser { .collect(); match matched_values.len() { - 0 => Err(self.generate_clap_error(cmd, arg, value)), + 0 => Err(self.generate_clap_error(cmd, arg, value, &[])), 1 => Ok(matched_values[0].get_name().to_string()), _ => { if let Some(direct_match) = matched_values.iter().find(|x| x.get_name() == value) { Ok(direct_match.get_name().to_string()) } else { - Err(self.generate_clap_error(cmd, arg, value)) + Err(self.generate_clap_error(cmd, arg, value, &matched_values)) } } } @@ -143,7 +164,11 @@ mod tests { for ambiguous_value in ambiguous_values { let result = parser.parse_ref(&cmd, None, OsStr::new(ambiguous_value)); - assert_eq!(ErrorKind::InvalidValue, result.unwrap_err().kind()); + assert_eq!(ErrorKind::InvalidValue, result.as_ref().unwrap_err().kind()); + assert!(result.unwrap_err().to_string().contains(&format!( + "It looks like '{}' could match several values. Did you mean 'abcd' or 'abef'?", + ambiguous_value + ))); } let result = parser.parse_ref(&cmd, None, OsStr::new("abc")); From 25e2b2ab654d0e5781a65fcb1088e7b47e10bea3 Mon Sep 17 00:00:00 2001 From: mhead Date: Sun, 28 Jul 2024 23:46:41 +0530 Subject: [PATCH 3/9] added tests for fail-none and ambiguous parse error --- tests/by-util/test_cp.rs | 29 ++++++++++++++++++----------- tests/by-util/test_mv.rs | 33 +++++++++++++-------------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 8781bab3cc..996a83b465 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -289,18 +289,25 @@ fn test_cp_arg_update_interactive_error() { #[test] fn test_cp_arg_update_none() { - for argument in ["--update=none", "--update=non", "--update=n"] { - let (at, mut ucmd) = at_and_ucmd!(); - - ucmd.arg(TEST_HELLO_WORLD_SOURCE) - .arg(TEST_HOW_ARE_YOU_SOURCE) - .arg(argument) - .succeeds() - .no_stderr() - .no_stdout(); + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg(TEST_HOW_ARE_YOU_SOURCE) + .arg("--update=none") + .succeeds() + .no_stderr() + .no_stdout(); + assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n"); +} - assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n"); - } +#[test] +fn test_cp_arg_update_none_fail() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg(TEST_HOW_ARE_YOU_SOURCE) + .arg("--update=none-fail") + .fails() + .stderr_contains(format!("not replacing '{}'", TEST_HOW_ARE_YOU_SOURCE)); + assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n"); } #[test] diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index 82741d5f00..afa7417e24 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -300,8 +300,7 @@ fn test_mv_interactive_no_clobber_force_last_arg_wins() { scene .ucmd() .args(&[file_a, file_b, "-f", "-i", "-n"]) - .fails() - .stderr_is(format!("mv: not replacing '{file_b}'\n")); + .succeeds(); scene .ucmd() @@ -349,12 +348,7 @@ fn test_mv_no_clobber() { at.touch(file_a); at.touch(file_b); - ucmd.arg("-n") - .arg(file_a) - .arg(file_b) - .fails() - .code_is(1) - .stderr_only(format!("mv: not replacing '{file_b}'\n")); + ucmd.arg("-n").arg(file_a).arg(file_b).succeeds(); assert!(at.file_exists(file_a)); assert!(at.file_exists(file_b)); @@ -863,14 +857,16 @@ fn test_mv_backup_off() { } #[test] -fn test_mv_backup_no_clobber_conflicting_options() { - new_ucmd!() - .arg("--backup") - .arg("--no-clobber") - .arg("file1") - .arg("file2") - .fails() - .usage_error("options --backup and --no-clobber are mutually exclusive"); +fn test_mv_backup_conflicting_options() { + for conflicting_opt in ["--no-clobber", "--update=none-fail", "--update=none"] { + new_ucmd!() + .arg("--backup") + .arg(conflicting_opt) + .arg("file1") + .arg("file2") + .fails() + .usage_error("cannot combine --backup with -n or --update=none-fail"); + } } #[test] @@ -1400,10 +1396,7 @@ fn test_mv_arg_interactive_skipped_vin() { let (at, mut ucmd) = at_and_ucmd!(); at.touch("a"); at.touch("b"); - ucmd.args(&["-vin", "a", "b"]) - .fails() - .stderr_is("mv: not replacing 'b'\n") - .no_stdout(); + ucmd.args(&["-vin", "a", "b"]).succeeds().no_stdout(); } #[test] From 0b7f3f2de9305c5dd849b307e3542cab9ec9e661 Mon Sep 17 00:00:00 2001 From: mhead Date: Mon, 29 Jul 2024 00:25:24 +0530 Subject: [PATCH 4/9] uucore: ambiguous value code refractor --- .../src/lib/parser/shortcut_value_parser.rs | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/uucore/src/lib/parser/shortcut_value_parser.rs b/src/uucore/src/lib/parser/shortcut_value_parser.rs index 4df2529815..267cf5b756 100644 --- a/src/uucore/src/lib/parser/shortcut_value_parser.rs +++ b/src/uucore/src/lib/parser/shortcut_value_parser.rs @@ -55,28 +55,37 @@ impl ShortcutValueParser { // if `possible_values` is not empty then that means this error is because of an ambiguous value. if !possible_values.is_empty() { - let mut formatted_possible_values = String::new(); - for (i, s) in possible_values.iter().enumerate() { - formatted_possible_values.push_str(&format!("'{}'", s.get_name())); - if i < possible_values.len() - 2 { - formatted_possible_values.push_str(", "); - } else if i < possible_values.len() - 1 { - formatted_possible_values.push_str(" or "); - } - } - err.insert( - ContextKind::Suggested, - ContextValue::StyledStrs(vec![format!( - "It looks like '{}' could match several values. Did you mean {}?", - value, formatted_possible_values - ) - .into()]), - ); + add_ambiguous_value_tip(possible_values, &mut err, value); } err } } +/// Adds a suggestion when error is because of ambiguous values based on the provided possible values. +fn add_ambiguous_value_tip( + possible_values: &[&PossibleValue], + err: &mut clap::error::Error, + value: &str, +) { + let mut formatted_possible_values = String::new(); + for (i, s) in possible_values.iter().enumerate() { + formatted_possible_values.push_str(&format!("'{}'", s.get_name())); + if i < possible_values.len() - 2 { + formatted_possible_values.push_str(", "); + } else if i < possible_values.len() - 1 { + formatted_possible_values.push_str(" or "); + } + } + err.insert( + ContextKind::Suggested, + ContextValue::StyledStrs(vec![format!( + "It looks like '{}' could match several values. Did you mean {}?", + value, formatted_possible_values + ) + .into()]), + ); +} + impl TypedValueParser for ShortcutValueParser { type Value = String; From a79cd66d3cf0960d707a67a812063b9b1b090a3e Mon Sep 17 00:00:00 2001 From: sreehari prasad <52113972+matrixhead@users.noreply.github.com> Date: Fri, 2 Aug 2024 17:29:48 +0530 Subject: [PATCH 5/9] Update src/uu/mv/src/mv.rs Co-authored-by: Sylvestre Ledru --- src/uu/mv/src/mv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index 903bc52138..4c994f0b80 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -142,7 +142,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { { return Err(UUsageError::new( 1, - "cannot combine --backup with -n or --update=none-fail", + "cannot combine --backup with -n/--no-clobber or --update=none-fail", )); } From 070b14f046dac0afc1b0bb3516a991a0c74275c8 Mon Sep 17 00:00:00 2001 From: sreehari prasad <52113972+matrixhead@users.noreply.github.com> Date: Fri, 2 Aug 2024 17:29:54 +0530 Subject: [PATCH 6/9] Update tests/by-util/test_mv.rs Co-authored-by: Sylvestre Ledru --- tests/by-util/test_mv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index afa7417e24..db5feff883 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -865,7 +865,7 @@ fn test_mv_backup_conflicting_options() { .arg("file1") .arg("file2") .fails() - .usage_error("cannot combine --backup with -n or --update=none-fail"); + .usage_error("cannot combine --backup with -n/--no-clobber or --update=none-fail"); } } From 6bf89d5bc5a1afb25d32c8220244a7852a2db0aa Mon Sep 17 00:00:00 2001 From: mhead Date: Fri, 16 Aug 2024 11:42:10 +0530 Subject: [PATCH 7/9] cp: no-clobber fail silently and outputs skipped message in debug --- src/uu/cp/src/cp.rs | 34 ++++++++++++++++++++-------------- tests/by-util/test_cp.rs | 26 ++++++++++++++++---------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index cd95761a56..0e78a757a6 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -38,7 +38,7 @@ use uucore::{backup_control, update_control}; pub use uucore::{backup_control::BackupMode, update_control::UpdateMode}; use uucore::{ format_usage, help_about, help_section, help_usage, prompt_yes, - shortcut_value_parser::ShortcutValueParser, show_error, show_warning, util_name, + shortcut_value_parser::ShortcutValueParser, show_error, show_warning, }; use crate::copydir::copy_directory; @@ -78,8 +78,10 @@ quick_error! { StripPrefixError(err: StripPrefixError) { from() } /// Result of a skipped file - /// Currently happens when "no" is selected in interactive mode - Skipped { } + /// Currently happens when "no" is selected in interactive mode or when + /// `no-clobber` flag is set and destination is already present. + /// `exit with error` is used to determine which exit code should be returned. + Skipped(exit_with_error:bool) { } /// Result of a skipped file InvalidArgument(description: String) { display("{}", description) } @@ -1207,7 +1209,7 @@ fn show_error_if_needed(error: &Error) { Error::NotAllFilesCopied => { // Need to return an error code } - Error::Skipped => { + Error::Skipped(_) => { // touch a b && echo "n"|cp -i a b && echo $? // should return an error from GNU 9.2 } @@ -1292,7 +1294,9 @@ pub fn copy(sources: &[PathBuf], target: &Path, options: &Options) -> CopyResult &mut copied_files, ) { show_error_if_needed(&error); - non_fatal_errors = true; + if !matches!(error, Error::Skipped(false)) { + non_fatal_errors = true; + } } else { copied_destinations.insert(dest.clone()); } @@ -1394,17 +1398,19 @@ fn copy_source( } impl OverwriteMode { - fn verify(&self, path: &Path) -> CopyResult<()> { + fn verify(&self, path: &Path, debug: bool) -> CopyResult<()> { match *self { Self::NoClobber => { - eprintln!("{}: not replacing {}", util_name(), path.quote()); - Err(Error::NotAllFilesCopied) + if debug { + println!("skipped {}", path.quote()); + } + Err(Error::Skipped(false)) } Self::Interactive(_) => { if prompt_yes!("overwrite {}?", path.quote()) { Ok(()) } else { - Err(Error::Skipped) + Err(Error::Skipped(true)) } } Self::Clobber(_) => Ok(()), @@ -1651,7 +1657,7 @@ fn handle_existing_dest( } if options.update != UpdateMode::ReplaceIfOlder { - options.overwrite.verify(dest)?; + options.overwrite.verify(dest, options.debug)?; } let mut is_dest_removed = false; @@ -1900,7 +1906,7 @@ fn handle_copy_mode( if src_time <= dest_time { return Ok(()); } else { - options.overwrite.verify(dest)?; + options.overwrite.verify(dest, options.debug)?; copy_helper( source, @@ -2262,7 +2268,7 @@ fn copy_helper( File::create(dest).context(dest.display().to_string())?; } else if source_is_fifo && options.recursive && !options.copy_contents { #[cfg(unix)] - copy_fifo(dest, options.overwrite)?; + copy_fifo(dest, options.overwrite, options.debug)?; } else if source_is_symlink { copy_link(source, dest, symlinked_files)?; } else { @@ -2287,9 +2293,9 @@ fn copy_helper( // "Copies" a FIFO by creating a new one. This workaround is because Rust's // built-in fs::copy does not handle FIFOs (see rust-lang/rust/issues/79390). #[cfg(unix)] -fn copy_fifo(dest: &Path, overwrite: OverwriteMode) -> CopyResult<()> { +fn copy_fifo(dest: &Path, overwrite: OverwriteMode, debug: bool) -> CopyResult<()> { if dest.exists() { - overwrite.verify(dest)?; + overwrite.verify(dest, debug)?; fs::remove_file(dest)?; } diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 996a83b465..175645d261 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -560,10 +560,9 @@ fn test_cp_arg_interactive_verbose_clobber() { let (at, mut ucmd) = at_and_ucmd!(); at.touch("a"); at.touch("b"); - ucmd.args(&["-vin", "a", "b"]) - .fails() - .stderr_is("cp: not replacing 'b'\n") - .no_stdout(); + ucmd.args(&["-vin", "--debug", "a", "b"]) + .succeeds() + .stdout_contains("skipped 'b'"); } #[test] @@ -662,8 +661,9 @@ fn test_cp_arg_no_clobber() { ucmd.arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE) .arg("--no-clobber") - .fails() - .stderr_contains("not replacing"); + .arg("--debug") + .succeeds() + .stdout_contains("skipped 'how_are_you.txt'"); assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n"); } @@ -674,7 +674,9 @@ fn test_cp_arg_no_clobber_inferred_arg() { ucmd.arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE) .arg("--no-clob") - .fails(); + .arg("--debug") + .succeeds() + .stdout_contains("skipped 'how_are_you.txt'"); assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n"); } @@ -690,6 +692,7 @@ fn test_cp_arg_no_clobber_twice() { .arg("--no-clobber") .arg("source.txt") .arg("dest.txt") + .arg("--debug") .succeeds() .no_stderr(); @@ -701,7 +704,9 @@ fn test_cp_arg_no_clobber_twice() { .arg("--no-clobber") .arg("source.txt") .arg("dest.txt") - .fails(); + .arg("--debug") + .succeeds() + .stdout_contains("skipped 'dest.txt'"); assert_eq!(at.read("source.txt"), "some-content"); // Should be empty as the "no-clobber" should keep @@ -1745,11 +1750,12 @@ fn test_cp_preserve_links_case_7() { ucmd.arg("-n") .arg("--preserve=links") + .arg("--debug") .arg("src/f") .arg("src/g") .arg("dest") - .fails() - .stderr_contains("not replacing"); + .succeeds() + .stdout_contains("skipped 'dest/g'"); assert!(at.dir_exists("dest")); assert!(at.plus("dest").join("f").exists()); From ea3f6837261459905653027423fb9330eed28bf9 Mon Sep 17 00:00:00 2001 From: mhead Date: Fri, 16 Aug 2024 12:42:02 +0530 Subject: [PATCH 8/9] mv: add --debug support --- src/uu/mv/src/mv.rs | 19 ++++++++++++++++++- tests/by-util/test_mv.rs | 16 ++++++++++++---- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index 4c994f0b80..5f1b717834 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -83,6 +83,9 @@ pub struct Options { /// '-g, --progress' pub progress_bar: bool, + + /// `--debug` + pub debug: bool, } /// specifies behavior of the overwrite flag @@ -109,6 +112,7 @@ static OPT_NO_TARGET_DIRECTORY: &str = "no-target-directory"; static OPT_VERBOSE: &str = "verbose"; static OPT_PROGRESS: &str = "progress"; static ARG_FILES: &str = "files"; +static OPT_DEBUG: &str = "debug"; #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { @@ -165,9 +169,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { update: update_mode, target_dir, no_target_dir: matches.get_flag(OPT_NO_TARGET_DIRECTORY), - verbose: matches.get_flag(OPT_VERBOSE), + verbose: matches.get_flag(OPT_VERBOSE) || matches.get_flag(OPT_DEBUG), strip_slashes: matches.get_flag(OPT_STRIP_TRAILING_SLASHES), progress_bar: matches.get_flag(OPT_PROGRESS), + debug: matches.get_flag(OPT_DEBUG), }; mv(&files[..], &opts) @@ -260,6 +265,12 @@ pub fn uu_app() -> Command { .value_parser(ValueParser::os_string()) .value_hint(clap::ValueHint::AnyPath), ) + .arg( + Arg::new(OPT_DEBUG) + .long(OPT_DEBUG) + .help("explain how a file is copied. Implies -v") + .action(ArgAction::SetTrue), + ) } fn determine_overwrite_mode(matches: &ArgMatches) -> OverwriteMode { @@ -525,6 +536,9 @@ fn rename( } if opts.update == UpdateMode::ReplaceNone { + if opts.debug { + println!("skipped {}", to.quote()); + } return Ok(()); } @@ -541,6 +555,9 @@ fn rename( match opts.overwrite { OverwriteMode::NoClobber => { + if opts.debug { + println!("skipped {}", to.quote()); + } return Ok(()); } OverwriteMode::Interactive => { diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index db5feff883..7b100fe5c3 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -299,8 +299,9 @@ fn test_mv_interactive_no_clobber_force_last_arg_wins() { scene .ucmd() - .args(&[file_a, file_b, "-f", "-i", "-n"]) - .succeeds(); + .args(&[file_a, file_b, "-f", "-i", "-n", "--debug"]) + .succeeds() + .stdout_contains("skipped 'b.txt'"); scene .ucmd() @@ -348,7 +349,12 @@ fn test_mv_no_clobber() { at.touch(file_a); at.touch(file_b); - ucmd.arg("-n").arg(file_a).arg(file_b).succeeds(); + ucmd.arg("-n") + .arg(file_a) + .arg(file_b) + .arg("--debug") + .succeeds() + .stdout_contains("skipped 'test_mv_no_clobber_file_b"); assert!(at.file_exists(file_a)); assert!(at.file_exists(file_b)); @@ -1396,7 +1402,9 @@ fn test_mv_arg_interactive_skipped_vin() { let (at, mut ucmd) = at_and_ucmd!(); at.touch("a"); at.touch("b"); - ucmd.args(&["-vin", "a", "b"]).succeeds().no_stdout(); + ucmd.args(&["-vin", "a", "b", "--debug"]) + .succeeds() + .stdout_contains("skipped 'b'"); } #[test] From a9c31d34827365a068a6d8faf325c08e5fe75507 Mon Sep 17 00:00:00 2001 From: mhead Date: Fri, 16 Aug 2024 14:13:29 +0530 Subject: [PATCH 9/9] minor changes --- tests/by-util/test_cp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 175645d261..ac0092504c 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -1755,7 +1755,7 @@ fn test_cp_preserve_links_case_7() { .arg("src/g") .arg("dest") .succeeds() - .stdout_contains("skipped 'dest/g'"); + .stdout_contains("skipped"); assert!(at.dir_exists("dest")); assert!(at.plus("dest").join("f").exists());