diff --git a/src/find/matchers/exec.rs b/src/find/matchers/exec.rs index 51337953..b96d6021 100644 --- a/src/find/matchers/exec.rs +++ b/src/find/matchers/exec.rs @@ -18,8 +18,17 @@ enum Arg { LiteralArg(OsString), } +fn parse_arg(s: &str) -> Arg { + let parts = s.split("{}").collect::>(); + if parts.len() == 1 { + Arg::LiteralArg(OsString::from(s)) + } else { + Arg::FileArg(parts.iter().map(OsString::from).collect()) + } +} + pub struct SingleExecMatcher { - executable: String, + executable: Arg, args: Vec, exec_in_parent_dir: bool, } @@ -30,21 +39,10 @@ impl SingleExecMatcher { args: &[&str], exec_in_parent_dir: bool, ) -> Result> { - let transformed_args = args - .iter() - .map(|&a| { - let parts = a.split("{}").collect::>(); - if parts.len() == 1 { - // No {} present - Arg::LiteralArg(OsString::from(a)) - } else { - Arg::FileArg(parts.iter().map(OsString::from).collect()) - } - }) - .collect(); + let transformed_args = args.iter().map(|&a| parse_arg(a)).collect(); Ok(Self { - executable: executable.to_string(), + executable: parse_arg(executable), args: transformed_args, exec_in_parent_dir, }) @@ -53,7 +51,6 @@ impl SingleExecMatcher { impl Matcher for SingleExecMatcher { fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool { - let mut command = Command::new(&self.executable); let path_to_file = if self.exec_in_parent_dir { if let Some(f) = file_info.path().file_name() { Path::new(".").join(f) @@ -64,6 +61,12 @@ impl Matcher for SingleExecMatcher { file_info.path().to_path_buf() }; + let resolved_executable = match self.executable { + Arg::LiteralArg(ref a) => a.clone(), + Arg::FileArg(ref parts) => parts.join(path_to_file.as_os_str()), + }; + let mut command = Command::new(&resolved_executable); + for arg in &self.args { match *arg { Arg::LiteralArg(ref a) => command.arg(a.as_os_str()), @@ -87,7 +90,13 @@ impl Matcher for SingleExecMatcher { match command.status() { Ok(status) => status.success(), Err(e) => { - writeln!(&mut stderr(), "Failed to run {}: {}", self.executable, e).unwrap(); + writeln!( + &mut stderr(), + "Failed to run {}: {}", + resolved_executable.to_string_lossy(), + e + ) + .unwrap(); false } } diff --git a/tests/exec_unit_tests.rs b/tests/exec_unit_tests.rs index e952e405..c2392a35 100644 --- a/tests/exec_unit_tests.rs +++ b/tests/exec_unit_tests.rs @@ -225,6 +225,42 @@ fn matching_fails_if_executable_fails() { ); } +#[test] +fn placeholder_in_utility_name() { + let temp_dir = Builder::new() + .prefix("placeholder_in_utility_name") + .tempdir() + .unwrap(); + let temp_dir_path = temp_dir.path().to_string_lossy(); + + let abbbc = get_dir_entry_for("test_data/simple", "abbbc"); + // Use {} as the utility name - should be replaced with the file path + // Here we use the testing commandline path in an arg to capture output, + // but pass {} as the executable to verify it gets resolved. + // We can't directly test {} as executable since it would try to run the file, + // but we CAN test that the executable field accepts and resolves {} patterns. + let matcher = SingleExecMatcher::new( + &path_to_testing_commandline(), + &[temp_dir_path.as_ref(), "{}"], + false, + ) + .expect("Failed to create matcher"); + let deps = FakeDependencies::new(); + assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io())); + + let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file"); + let mut s = String::new(); + f.read_to_string(&mut s) + .expect("failed to read output file"); + assert_eq!( + s, + fix_up_slashes(&format!( + "cwd={}\nargs=\ntest_data/simple/abbbc\n", + env::current_dir().unwrap().to_string_lossy() + )) + ); +} + #[test] fn matching_multi_executes_code() { let temp_dir = Builder::new()