feat: conditional suggestions

This commit is contained in:
iff 2023-07-31 09:24:46 +02:00
parent 72db07051b
commit 6b4d926dde
5 changed files with 54 additions and 19 deletions

View file

@ -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:

View file

@ -20,7 +20,7 @@ struct Rule {
#[derive(serde::Deserialize)]
struct MatchOutput {
pattern: Vec<String>,
suggest: String,
suggest: Vec<String>,
}
fn get_rules(directory: String) -> Vec<Rule> {
@ -51,9 +51,9 @@ fn gen_string_hashmap(rules: Vec<Rule>) -> String {
.collect::<Vec<String>>();
let suggest = match_output.suggest;
string_hashmap.push_str(&format!(
"(vec![\"{}\"], \"{}\"),",
"(vec![\"{}\"], vec![\"{}\"]),",
pattern.join("\", \""),
suggest
suggest.join("\", \"")
));
}
string_hashmap.push_str("]),");

View file

@ -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}}''',
]

View file

@ -39,7 +39,11 @@ fn match_pattern(command: &str, error_msg: &str) -> Option<String> {
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<String> {
}
}
fn check_suggest(suggest: &str) -> Option<String> {
if !suggest.starts_with('#') {
return Some(suggest.to_owned());
}
let lines = suggest.lines().collect::<Vec<&str>>();
let conditions = lines.first().unwrap();
let conditions = conditions.trim_matches(|c| c == '#' || c == '[' || c == ']');
let conditions = conditions.split(',').collect::<Vec<&str>>();
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")

View file

@ -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!(