From a85f5d7e0194a43b98271941430089539cbc16d5 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 22 Feb 2024 22:53:53 +0100 Subject: [PATCH 1/9] parse_delimiter: Make the code more idiomatic --- src/xargs/mod.rs | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/xargs/mod.rs b/src/xargs/mod.rs index a923570..ebd2c4e 100644 --- a/src/xargs/mod.rs +++ b/src/xargs/mod.rs @@ -692,12 +692,14 @@ fn process_input( } fn parse_delimiter(s: &str) -> Result { - if let Some(hex) = s.strip_prefix("\\x") { - u8::from_str_radix(hex, 16).map_err(|e| format!("Invalid hex sequence: {}", e)) - } else if let Some(oct) = s.strip_prefix("\\0") { - u8::from_str_radix(oct, 8).map_err(|e| format!("Invalid octal sequence: {}", e)) - } else if let Some(special) = s.strip_prefix('\\') { - match special { + match s.strip_prefix('\\') { + Some(hex) if hex.starts_with('x') => { + u8::from_str_radix(&hex[1..], 16).map_err(|e| format!("Invalid hex sequence: {}", e)) + } + Some(oct) if oct.starts_with('0') => { + u8::from_str_radix(&oct[1..], 8).map_err(|e| format!("Invalid octal sequence: {}", e)) + } + Some(special) => match special { "a" => Ok(b'\x07'), "b" => Ok(b'\x08'), "f" => Ok(b'\x0C'), @@ -705,17 +707,12 @@ fn parse_delimiter(s: &str) -> Result { "r" => Ok(b'\r'), "t" => Ok(b'\t'), "v" => Ok(b'\x0B'), - "0" => Ok(b'\0'), "\\" => Ok(b'\\'), - _ => Err(format!("Invalid escape sequence: {s}")), - } - } else { - let bytes = s.as_bytes(); - if bytes.len() == 1 { - Ok(bytes[0]) - } else { - Err("Delimiter must be one byte".to_owned()) - } + "0" => Ok(b'\0'), + _ => Err(format!("Invalid escape sequence: \\{}", special)), + }, + None if s.len() == 1 => Ok(s.as_bytes()[0]), + None => Err("Delimiter must be one byte".to_owned()), } } From 9b3514a100d331313da78e93e0f807ba3155d6b6 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 16 Mar 2024 13:47:09 +0100 Subject: [PATCH 2/9] xargs: Implement -I to replace (Closes: #310) --- src/xargs/mod.rs | 79 ++++++++++++++++++++++++++++++++++++-------- tests/xargs_tests.rs | 31 +++++++++++++++++ 2 files changed, 96 insertions(+), 14 deletions(-) diff --git a/src/xargs/mod.rs b/src/xargs/mod.rs index ebd2c4e..14a9381 100644 --- a/src/xargs/mod.rs +++ b/src/xargs/mod.rs @@ -28,6 +28,7 @@ mod options { pub const MAX_PROCS: &str = "max-procs"; pub const NO_RUN_IF_EMPTY: &str = "no-run-if-empty"; pub const NULL: &str = "null"; + pub const REPLACE: &str = "replace"; pub const VERBOSE: &str = "verbose"; } @@ -40,6 +41,7 @@ struct Options { max_lines: Option, no_run_if_empty: bool, null: bool, + replace: Option, verbose: bool, } @@ -340,13 +342,14 @@ struct CommandBuilderOptions { limiters: LimiterCollection, verbose: bool, close_stdin: bool, + replace: Option, } - impl CommandBuilderOptions { fn new( action: ExecAction, env: HashMap, mut limiters: LimiterCollection, + replace: Option, ) -> Result { let initial_args = match &action { ExecAction::Command(args) => args.iter().map(|arg| arg.as_ref()).collect(), @@ -366,6 +369,7 @@ impl CommandBuilderOptions { limiters, verbose: false, close_stdin: false, + replace, }) } } @@ -398,14 +402,41 @@ impl CommandBuilder<'_> { }; let mut command = Command::new(entry_point); - command - .args(initial_args) - .args(&self.extra_args) - .env_clear() - .envs(&self.options.env); + + if let Some(replace_str) = &self.options.replace { + // we replace the first instance of the replacement string with + // the extra args, and then replace all instances of the replacement + let replacement = self + .extra_args + .iter() + .map(|s| s.to_string_lossy()) + .collect::>() + .join(" "); + let initial_args: Vec = initial_args + .iter() + .map(|arg| { + let arg_str = arg.to_string_lossy(); + OsString::from(arg_str.replace(replace_str, &replacement)) + }) + .collect(); + + command + .args(&initial_args) + .env_clear() + .envs(&self.options.env); + } else { + // don't do any replacement + command + .args(initial_args) + .args(&self.extra_args) + .env_clear() + .envs(&self.options.env); + }; + if self.options.close_stdin { command.stdin(Stdio::null()); } + if self.options.verbose { eprintln!("{command:?}"); } @@ -811,11 +842,21 @@ fn do_xargs(args: &[&str]) -> Result { ) .arg( Arg::new(options::VERBOSE) - .short('t') - .long(options::VERBOSE) - .help("Be verbose") - .action(ArgAction::SetTrue), + .short('t') + .long(options::VERBOSE) + .help("Be verbose") + .action(ArgAction::SetTrue), ) + .arg( + Arg::new(options::REPLACE) + .long(options::REPLACE) + .short('I') + .short_alias('i') + .num_args(0..=1) + .value_parser(clap::value_parser!(String)) + .help("Replace R in INITIAL-ARGS with names read from standard input; if R is unspecified, assume {}"), + ) + .try_get_matches_from(args); let matches = match matches { @@ -834,6 +875,16 @@ fn do_xargs(args: &[&str]) -> Result { max_lines: matches.get_one::(options::MAX_LINES).copied(), no_run_if_empty: matches.get_flag(options::NO_RUN_IF_EMPTY), null: matches.get_flag(options::NULL), + replace: if matches.contains_id(options::REPLACE) { + Some( + matches + .get_one::(options::REPLACE) + .map(|value| value.to_owned()) + .unwrap_or("{}".to_string()), + ) + } else { + None + }, verbose: matches.get_flag(options::VERBOSE), }; @@ -858,7 +909,6 @@ fn do_xargs(args: &[&str]) -> Result { } _ => ExecAction::Echo, }; - let env = std::env::vars_os().collect(); let mut limiters = LimiterCollection::new(); @@ -888,9 +938,10 @@ fn do_xargs(args: &[&str]) -> Result { limiters.add(MaxCharsCommandSizeLimiter::new_system(&env)); - let mut builder_options = CommandBuilderOptions::new(action, env, limiters).map_err(|_| { - "Base command and environment are too large to fit into one command execution" - })?; + let mut builder_options = + CommandBuilderOptions::new(action, env, limiters, options.replace.clone()).map_err( + |_| "Base command and environment are too large to fit into one command execution", + )?; builder_options.verbose = options.verbose; builder_options.close_stdin = options.arg_file.is_none(); diff --git a/tests/xargs_tests.rs b/tests/xargs_tests.rs index a4eb549..de4b027 100644 --- a/tests/xargs_tests.rs +++ b/tests/xargs_tests.rs @@ -385,3 +385,34 @@ fn xargs_zero_lines() { .stderr(predicate::str::contains("Value must be > 0, not: 0")) .stdout(predicate::str::is_empty()); } + +#[test] +fn xargs_replace() { + Command::cargo_bin("xargs") + .expect("found binary") + .args(["-I", "{}", "echo", "{} bar"]) + .write_stdin("foo") + .assert() + .stdout(predicate::str::contains("foo bar")); + + Command::cargo_bin("xargs") + .expect("found binary") + .args(["-I", "_", "echo", "_ bar"]) + .write_stdin("foo") + .assert() + .stdout(predicate::str::contains("foo bar")); + + Command::cargo_bin("xargs") + .expect("found binary") + .args(["--replace", "_", "echo", "_ _ bar"]) + .write_stdin("foo") + .assert() + .stdout(predicate::str::contains("foo foo bar")); + + Command::cargo_bin("xargs") + .expect("found binary") + .args(["-i", "_", "echo", "_ _ bar"]) + .write_stdin("foo") + .assert() + .stdout(predicate::str::contains("foo foo bar")); +} From e64c69a7038a4052c74260b1cc4f1ecd3f23c2eb Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 24 Mar 2024 21:39:57 +0100 Subject: [PATCH 3/9] use pretty_assertions --- Cargo.lock | 23 +++++++++++++++++++++++ Cargo.toml | 1 + tests/xargs_tests.rs | 1 + 3 files changed, 25 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 53a7fbc..bc01057 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,6 +215,12 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "difflib" version = "0.4.0" @@ -285,6 +291,7 @@ dependencies = [ "once_cell", "onig", "predicates", + "pretty_assertions", "regex", "serial_test", "tempfile", @@ -591,6 +598,16 @@ dependencies = [ "treeline", ] +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro2" version = "1.0.60" @@ -1058,3 +1075,9 @@ name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/Cargo.toml b/Cargo.toml index cf1a045..8596547 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ nix = { version = "0.28", features = ["fs"] } predicates = "3" serial_test = "3.0" tempfile = "3" +pretty_assertions = "1.4.0" [[bin]] name = "find" diff --git a/tests/xargs_tests.rs b/tests/xargs_tests.rs index de4b027..8c28dda 100644 --- a/tests/xargs_tests.rs +++ b/tests/xargs_tests.rs @@ -12,6 +12,7 @@ use assert_cmd::Command; use predicates::prelude::*; use common::test_helpers::path_to_testing_commandline; +use pretty_assertions::assert_eq; mod common; From 0c2bb89770a52f253b41c73f76ab702e06eb1c70 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 24 Mar 2024 22:41:05 +0100 Subject: [PATCH 4/9] tests: improve test output in case of error --- tests/xargs_tests.rs | 107 +++++++++++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 34 deletions(-) diff --git a/tests/xargs_tests.rs b/tests/xargs_tests.rs index 8c28dda..c2b0961 100644 --- a/tests/xargs_tests.rs +++ b/tests/xargs_tests.rs @@ -209,7 +209,7 @@ fn xargs_exit_on_large() { #[test] fn xargs_exec() { - Command::cargo_bin("xargs") + let result = Command::cargo_bin("xargs") .expect("found binary") .args([ "-n2", @@ -219,13 +219,20 @@ fn xargs_exec() { "--no_print_cwd", ]) .write_stdin("a b c\nd") - .assert() - .success() - .stderr(predicate::str::is_empty()) - .stdout(predicate::str::diff( - "stdin=\nargs=\n--print_stdin\n--no_print_cwd\na\nb\n\ + .output(); + assert!(result.is_ok(), "xargs failed: {:?}", result); + let result = result.unwrap(); + assert_eq!(result.status.code(), Some(0)); + + assert!(result.stderr.is_empty(), "stderr: {:?}", result); + + let stdout_string = String::from_utf8(result.stdout).expect("Found invalid UTF-8"); + + assert_eq!( + stdout_string, + "stdin=\nargs=\n--print_stdin\n--no_print_cwd\na\nb\n\ stdin=\nargs=\n--print_stdin\n--no_print_cwd\nc\nd\n", - )); + ); } #[test] @@ -235,7 +242,7 @@ fn xargs_exec_stdin_open() { write!(temp_file, "a b c").unwrap(); temp_file.seek(SeekFrom::Start(0)).unwrap(); - Command::cargo_bin("xargs") + let result = Command::cargo_bin("xargs") .expect("found binary") .args([ "-a", @@ -246,17 +253,25 @@ fn xargs_exec_stdin_open() { "--no_print_cwd", ]) .write_stdin("test") - .assert() - .success() - .stderr(predicate::str::is_empty()) - .stdout(predicate::str::diff( - "stdin=test\nargs=\n--print_stdin\n--no_print_cwd\na\nb\nc\n", - )); + .output(); + + assert!(result.is_ok(), "xargs failed: {:?}", result); + let result = result.unwrap(); + assert_eq!(result.status.code(), Some(0)); + + assert!(result.stderr.is_empty(), "stderr: {:?}", result); + + let stdout_string = String::from_utf8(result.stdout).expect("Found invalid UTF-8"); + + assert_eq!( + stdout_string, + "stdin=test\nargs=\n--print_stdin\n--no_print_cwd\na\nb\nc\n", + ); } #[test] fn xargs_exec_failure() { - Command::cargo_bin("xargs") + let result = Command::cargo_bin("xargs") .expect("found binary") .args([ "-n1", @@ -266,19 +281,26 @@ fn xargs_exec_failure() { "--exit_with_failure", ]) .write_stdin("a b") - .assert() - .failure() - .code(123) - .stderr(predicate::str::is_empty()) - .stdout( - "args=\n--no_print_cwd\n--exit_with_failure\na\n\ + .output(); + + assert!(result.is_ok(), "xargs failed: {:?}", result); + let result = result.unwrap(); + assert_eq!(result.status.code(), Some(123)); + + assert!(result.stderr.is_empty(), "stderr: {:?}", result); + + let stdout_string = String::from_utf8(result.stdout).expect("Found invalid UTF-8"); + + assert_eq!( + stdout_string, + "args=\n--no_print_cwd\n--exit_with_failure\na\n\ args=\n--no_print_cwd\n--exit_with_failure\nb\n", - ); + ); } #[test] fn xargs_exec_urgent_failure() { - Command::cargo_bin("xargs") + let result = Command::cargo_bin("xargs") .expect("found binary") .args([ "-n1", @@ -288,17 +310,26 @@ fn xargs_exec_urgent_failure() { "--exit_with_urgent_failure", ]) .write_stdin("a b") - .assert() - .failure() - .code(124) - .stderr(predicate::str::contains("Error:")) - .stdout("args=\n--no_print_cwd\n--exit_with_urgent_failure\na\n"); + .output(); + + assert!(result.is_ok(), "xargs failed: {:?}", result); + let result = result.unwrap(); + assert_eq!(result.status.code(), Some(124)); + + assert!(!result.stderr.is_empty(), "stderr: {:?}", result); + + let stdout_string = String::from_utf8(result.stdout).expect("Found invalid UTF-8"); + + assert_eq!( + stdout_string, + "args=\n--no_print_cwd\n--exit_with_urgent_failure\na\n" + ); } #[test] #[cfg(unix)] fn xargs_exec_with_signal() { - Command::cargo_bin("xargs") + let result = Command::cargo_bin("xargs") .expect("found binary") .args([ "-n1", @@ -308,11 +339,19 @@ fn xargs_exec_with_signal() { "--exit_with_signal", ]) .write_stdin("a b") - .assert() - .failure() - .code(125) - .stderr(predicate::str::contains("Error:")) - .stdout("args=\n--no_print_cwd\n--exit_with_signal\na\n"); + .output(); + + assert!(result.is_ok(), "xargs failed: {:?}", result); + let result = result.unwrap(); + assert_eq!(result.status.code(), Some(125)); + assert!(!result.stderr.is_empty(), "stderr: {:?}", result); + + let stdout_string = String::from_utf8(result.stdout).expect("Found invalid UTF-8"); + + assert_eq!( + stdout_string, + "args=\n--no_print_cwd\n--exit_with_signal\na\n" + ); } #[test] From 90cee0fe37f2ef5df7b6e560305522787a9c3675 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 27 Mar 2024 11:02:19 +0100 Subject: [PATCH 5/9] Fix long line Co-authored-by: Daniel Hofstetter --- src/xargs/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/xargs/mod.rs b/src/xargs/mod.rs index 14a9381..06d77a1 100644 --- a/src/xargs/mod.rs +++ b/src/xargs/mod.rs @@ -854,7 +854,10 @@ fn do_xargs(args: &[&str]) -> Result { .short_alias('i') .num_args(0..=1) .value_parser(clap::value_parser!(String)) - .help("Replace R in INITIAL-ARGS with names read from standard input; if R is unspecified, assume {}"), + .help( + "Replace R in INITIAL-ARGS with names read from standard input; \ + if R is unspecified, assume {}", + ), ) .try_get_matches_from(args); From 299973809ae96f1e1ee1bb62e5ab8aa7bd1e29b4 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 31 Mar 2024 17:35:15 +0200 Subject: [PATCH 6/9] xargs -I: support -i/-I different behavior --- src/xargs/mod.rs | 41 ++++++++++++++++++++++++----------------- tests/xargs_tests.rs | 31 +++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/xargs/mod.rs b/src/xargs/mod.rs index 06d77a1..11cb1fc 100644 --- a/src/xargs/mod.rs +++ b/src/xargs/mod.rs @@ -29,6 +29,7 @@ mod options { pub const NO_RUN_IF_EMPTY: &str = "no-run-if-empty"; pub const NULL: &str = "null"; pub const REPLACE: &str = "replace"; + pub const REPLACE_I: &str = "replace-I"; pub const VERBOSE: &str = "verbose"; } @@ -842,24 +843,30 @@ fn do_xargs(args: &[&str]) -> Result { ) .arg( Arg::new(options::VERBOSE) - .short('t') - .long(options::VERBOSE) - .help("Be verbose") - .action(ArgAction::SetTrue), + .short('t') + .long(options::VERBOSE) + .help("Be verbose") + .action(ArgAction::SetTrue), ) .arg( Arg::new(options::REPLACE) .long(options::REPLACE) - .short('I') - .short_alias('i') + .short('i') .num_args(0..=1) + .require_equals(true) .value_parser(clap::value_parser!(String)) .help( "Replace R in INITIAL-ARGS with names read from standard input; \ if R is unspecified, assume {}", ), ) - + .arg( + Arg::new(options::REPLACE_I) + .short('I') + .num_args(1) + .hide(true) + .value_parser(clap::value_parser!(String)), + ) .try_get_matches_from(args); let matches = match matches { @@ -878,16 +885,16 @@ fn do_xargs(args: &[&str]) -> Result { max_lines: matches.get_one::(options::MAX_LINES).copied(), no_run_if_empty: matches.get_flag(options::NO_RUN_IF_EMPTY), null: matches.get_flag(options::NULL), - replace: if matches.contains_id(options::REPLACE) { - Some( - matches - .get_one::(options::REPLACE) - .map(|value| value.to_owned()) - .unwrap_or("{}".to_string()), - ) - } else { - None - }, + replace: [options::REPLACE, options::REPLACE_I] + .iter() + .find_map(|&option| { + matches.contains_id(option).then(|| { + matches + .get_one::(option) + .map(|value| value.to_owned()) + .unwrap_or_else(|| "{}".to_string()) + }) + }), verbose: matches.get_flag(options::VERBOSE), }; diff --git a/tests/xargs_tests.rs b/tests/xargs_tests.rs index c2b0961..f4a14d5 100644 --- a/tests/xargs_tests.rs +++ b/tests/xargs_tests.rs @@ -430,29 +430,52 @@ fn xargs_zero_lines() { fn xargs_replace() { Command::cargo_bin("xargs") .expect("found binary") - .args(["-I", "{}", "echo", "{} bar"]) + .args(["-i={}", "echo", "{} bar"]) .write_stdin("foo") .assert() .stdout(predicate::str::contains("foo bar")); Command::cargo_bin("xargs") .expect("found binary") - .args(["-I", "_", "echo", "_ bar"]) + .args(["-i=_", "echo", "_ bar"]) .write_stdin("foo") .assert() .stdout(predicate::str::contains("foo bar")); Command::cargo_bin("xargs") .expect("found binary") - .args(["--replace", "_", "echo", "_ _ bar"]) + .args(["--replace=_", "echo", "_ _ bar"]) .write_stdin("foo") .assert() .stdout(predicate::str::contains("foo foo bar")); Command::cargo_bin("xargs") .expect("found binary") - .args(["-i", "_", "echo", "_ _ bar"]) + .args(["-i=_", "echo", "_ _ bar"]) .write_stdin("foo") .assert() .stdout(predicate::str::contains("foo foo bar")); + + Command::cargo_bin("xargs") + .expect("found binary") + .args(["-i", "echo", "{} {} bar"]) + .write_stdin("foo") + .assert() + .stdout(predicate::str::contains("foo foo bar")); + + Command::cargo_bin("xargs") + .expect("found binary") + .args(["-I={}", "echo", "{} bar {}"]) + .write_stdin("foo") + .assert() + .stdout(predicate::str::contains("foo bar foo")); + + // Excepted to fail + Command::cargo_bin("xargs") + .expect("found binary") + .args(["-I", "echo", "_ _ bar"]) + .write_stdin("foo") + .assert() + .failure() + .stderr(predicate::str::contains("Error: Command not found")); } From 21e15c301508d511ce939dc9206caeee638547ce Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 31 Mar 2024 17:42:00 +0200 Subject: [PATCH 7/9] xargs -I: support -i/-I add a corner case test --- tests/xargs_tests.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/xargs_tests.rs b/tests/xargs_tests.rs index f4a14d5..f620ac4 100644 --- a/tests/xargs_tests.rs +++ b/tests/xargs_tests.rs @@ -470,6 +470,14 @@ fn xargs_replace() { .assert() .stdout(predicate::str::contains("foo bar foo")); + // Combine the two options to see which one wins + Command::cargo_bin("xargs") + .expect("found binary") + .args(["-I=_", "-i", "echo", "{} bar {}"]) + .write_stdin("foo") + .assert() + .stdout(predicate::str::contains("foo bar foo")); + // Excepted to fail Command::cargo_bin("xargs") .expect("found binary") From a123a94c7d07385b5dde98021f33f42fec7e0786 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 2 Apr 2024 17:34:17 +0200 Subject: [PATCH 8/9] Improve the help Co-authored-by: Daniel Hofstetter --- src/xargs/mod.rs | 5 ++++- tests/xargs_tests.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/xargs/mod.rs b/src/xargs/mod.rs index 11cb1fc..6a269bd 100644 --- a/src/xargs/mod.rs +++ b/src/xargs/mod.rs @@ -855,6 +855,7 @@ fn do_xargs(args: &[&str]) -> Result { .num_args(0..=1) .require_equals(true) .value_parser(clap::value_parser!(String)) + .value_name("R") .help( "Replace R in INITIAL-ARGS with names read from standard input; \ if R is unspecified, assume {}", @@ -864,7 +865,9 @@ fn do_xargs(args: &[&str]) -> Result { Arg::new(options::REPLACE_I) .short('I') .num_args(1) - .hide(true) + .help("same as --replace=R") + .value_name("R") + .overrides_with(options::REPLACE) .value_parser(clap::value_parser!(String)), ) .try_get_matches_from(args); diff --git a/tests/xargs_tests.rs b/tests/xargs_tests.rs index f620ac4..28e7b47 100644 --- a/tests/xargs_tests.rs +++ b/tests/xargs_tests.rs @@ -478,7 +478,7 @@ fn xargs_replace() { .assert() .stdout(predicate::str::contains("foo bar foo")); - // Excepted to fail + // Expected to fail Command::cargo_bin("xargs") .expect("found binary") .args(["-I", "echo", "_ _ bar"]) From 45c1938eefa3176609ade8871f94e5f9afcfd491 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 2 Apr 2024 21:00:53 +0200 Subject: [PATCH 9/9] xargs: add more tests and priority --- src/xargs/mod.rs | 2 +- tests/xargs_tests.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/xargs/mod.rs b/src/xargs/mod.rs index 6a269bd..74144b7 100644 --- a/src/xargs/mod.rs +++ b/src/xargs/mod.rs @@ -888,7 +888,7 @@ fn do_xargs(args: &[&str]) -> Result { max_lines: matches.get_one::(options::MAX_LINES).copied(), no_run_if_empty: matches.get_flag(options::NO_RUN_IF_EMPTY), null: matches.get_flag(options::NULL), - replace: [options::REPLACE, options::REPLACE_I] + replace: [options::REPLACE_I, options::REPLACE] .iter() .find_map(|&option| { matches.contains_id(option).then(|| { diff --git a/tests/xargs_tests.rs b/tests/xargs_tests.rs index 28e7b47..fb6aa9a 100644 --- a/tests/xargs_tests.rs +++ b/tests/xargs_tests.rs @@ -478,6 +478,21 @@ fn xargs_replace() { .assert() .stdout(predicate::str::contains("foo bar foo")); + // other order + Command::cargo_bin("xargs") + .expect("found binary") + .args(["-i", "-I=_", "echo", "{} bar {}"]) + .write_stdin("foo") + .assert() + .stdout(predicate::str::contains("{} bar {}")); + + Command::cargo_bin("xargs") + .expect("found binary") + .args(["-i", "-I", "_", "echo", "{} bar _"]) + .write_stdin("foo") + .assert() + .stdout(predicate::str::contains("{} bar foo")); + // Expected to fail Command::cargo_bin("xargs") .expect("found binary")