Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 25 additions & 16 deletions src/find/matchers/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,17 @@ enum Arg {
LiteralArg(OsString),
}

fn parse_arg(s: &str) -> Arg {
let parts = s.split("{}").collect::<Vec<_>>();
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<Arg>,
exec_in_parent_dir: bool,
}
Expand All @@ -30,21 +39,10 @@ impl SingleExecMatcher {
args: &[&str],
exec_in_parent_dir: bool,
) -> Result<Self, Box<dyn Error>> {
let transformed_args = args
.iter()
.map(|&a| {
let parts = a.split("{}").collect::<Vec<_>>();
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,
})
Expand All @@ -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)
Expand All @@ -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()),
Expand All @@ -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
}
}
Expand Down
36 changes: 36 additions & 0 deletions tests/exec_unit_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading