diff --git a/README.md b/README.md index 41bee3c..809f94d 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,10 @@ suggest = "{{command[0]}} fix {{command[2:]}}" pattern = [ "pattern 1", ] -# this will add a `sudo` before the command, without touching the rest -suggest = "sudo {{command}}" +# this will add a `sudo` before the command if the `sudo` is found by `which` +suggest = ''' +#[executable(sudo)] +sudo {{command}}''' ``` The placeholder is evaluated as following: diff --git a/rule_parser/src/lib.rs b/rule_parser/src/lib.rs index 1c7c6f7..761424a 100644 --- a/rule_parser/src/lib.rs +++ b/rule_parser/src/lib.rs @@ -20,7 +20,7 @@ struct Rule { #[derive(serde::Deserialize)] struct MatchOutput { pattern: Vec, - suggest: String, + suggest: Vec, } fn get_rules(directory: String) -> Vec { @@ -51,9 +51,9 @@ fn gen_string_hashmap(rules: Vec) -> String { .collect::>(); let suggest = match_output.suggest; string_hashmap.push_str(&format!( - "(vec![\"{}\"], \"{}\"),", + "(vec![\"{}\"], vec![\"{}\"]),", pattern.join("\", \""), - suggest + suggest.join("\", \"") )); } string_hashmap.push_str("]),"); diff --git a/rules/sudo-doas.toml b/rules/sudo-doas.toml index 1bffe75..e73d928 100644 --- a/rules/sudo-doas.toml +++ b/rules/sudo-doas.toml @@ -27,4 +27,11 @@ pattern = [ "you don't have access to the history db.", "you don't have write permissions", ] -suggest = 'sudo {{command}}' +suggest = [ +''' +#[executable(sudo)] +sudo {{command}}''', +''' +#[executable(doas)] +doas {{command}}''', +] diff --git a/src/corrections.rs b/src/corrections.rs index 37a4e0e..e3cd642 100644 --- a/src/corrections.rs +++ b/src/corrections.rs @@ -39,7 +39,11 @@ fn match_pattern(command: &str, error_msg: &str) -> Option { for (pattern, suggest) in suggest { for pattern in pattern { if error_msg.contains(pattern) { - return Some(suggest.to_owned().to_string()); + for suggest in suggest { + if let Some(suggest) = check_suggest(suggest) { + return Some(suggest); + } + } } } } @@ -49,6 +53,39 @@ fn match_pattern(command: &str, error_msg: &str) -> Option { } } +fn check_suggest(suggest: &str) -> Option { + if !suggest.starts_with('#') { + return Some(suggest.to_owned()); + } + let lines = suggest.lines().collect::>(); + let conditions = lines.first().unwrap(); + let conditions = conditions.trim_matches(|c| c == '#' || c == '[' || c == ']'); + let conditions = conditions.split(',').collect::>(); + for condition in conditions { + let condition = condition.trim(); + let (condition, arg) = condition.split_once('(').unwrap(); + let arg = arg.trim_matches(|c| c == '(' || c == ')'); + + if eval_condition(condition, arg) == false { + return None; + } + } + Some(lines[1..].join("\n")) +} + +fn eval_condition(condition: &str, arg: &str) -> bool { + match condition { + "executable" => { + let output = std::process::Command::new("which") + .arg(arg) + .output() + .expect("failed to execute process"); + output.status.success() + } + _ => false, + } +} + fn eval_suggest(suggest: &str, last_command: &str) -> String { let mut suggest = suggest.to_owned(); if suggest.contains("{{command}}") { @@ -95,7 +132,6 @@ pub fn confirm_correction(shell: &str, command: &str, last_command: &str) { for p in PRIVILEGE_LIST { if command.starts_with(p) { let command = command.replace(p, ""); - println!("{} {}", p, command); std::process::Command::new(p.trim()) .arg(shell) .arg("-c") diff --git a/src/main.rs b/src/main.rs index a134f5e..3a09872 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,6 @@ mod corrections; mod shell; mod style; -use shell::get_privilege; - fn main() { std::env::set_var("LC_ALL", "C"); @@ -11,15 +9,7 @@ fn main() { let last_command = shell::find_last_command(&shell); let corrected_command = corrections::correct_command(&shell, &last_command); - if let Some(mut corrected_command) = corrected_command { - if corrected_command.starts_with("sudo ") { - let privilege = get_privilege(); - if let Some(privilege) = privilege { - if privilege != "sudo" { - corrected_command = corrected_command.replacen("sudo", &privilege, 1); - } - } - } + if let Some(corrected_command) = corrected_command { corrections::confirm_correction(&shell, &corrected_command, &last_command); } else { println!(