feat: allow regex in conditions

This commit is contained in:
iff 2025-04-09 17:16:58 +02:00
parent 3d80ecf9b8
commit b5c19f9a36
4 changed files with 28 additions and 12 deletions

View file

@ -29,7 +29,6 @@ pub fn runtime_match(
let split_command = split_command(last_command); let split_command = split_command(last_command);
let mut pure_suggest; let mut pure_suggest;
for match_err in rule.match_err { for match_err in rule.match_err {
for pattern in match_err.pattern { for pattern in match_err.pattern {
if error_msg.contains(&pattern) { if error_msg.contains(&pattern) {
@ -53,7 +52,11 @@ pub fn runtime_match(
for condition in conditions { for condition in conditions {
let (mut condition, arg) = condition.split_once('(').unwrap(); let (mut condition, arg) = condition.split_once('(').unwrap();
condition = condition.trim(); condition = condition.trim();
let arg = arg.trim_start_matches('(').trim_end_matches(')'); let arg = arg
.to_string()
.chars()
.take(arg.len() - 1)
.collect::<String>();
let reverse = match condition.starts_with('!') { let reverse = match condition.starts_with('!') {
true => { true => {
condition = condition.trim_start_matches('!'); condition = condition.trim_start_matches('!');
@ -63,7 +66,7 @@ pub fn runtime_match(
}; };
if eval_condition( if eval_condition(
condition, condition,
arg, &arg,
shell, shell,
last_command, last_command,
error_msg, error_msg,
@ -79,6 +82,7 @@ pub fn runtime_match(
} else { } else {
pure_suggest = suggest.to_owned(); pure_suggest = suggest.to_owned();
} }
// replacing placeholders // replacing placeholders
if pure_suggest.contains("{{command}}") { if pure_suggest.contains("{{command}}") {
pure_suggest = pure_suggest.replace("{{command}}", last_command); pure_suggest = pure_suggest.replace("{{command}}", last_command);
@ -90,6 +94,7 @@ pub fn runtime_match(
print!("<_PR_BR>"); print!("<_PR_BR>");
} }
} }
break;
} }
} }
} }
@ -106,9 +111,8 @@ fn eval_condition(
) -> bool { ) -> bool {
match condition { match condition {
"executable" => executables.contains(&arg.to_string()), "executable" => executables.contains(&arg.to_string()),
"err_contains" => error_msg.contains(arg), "err_contains" => regex_match(arg, error_msg),
"cmd_contains" => last_command.contains(arg), "cmd_contains" => regex_match(arg, last_command),
"exe_contains" => split_command[0].contains(arg),
"min_length" => split_command.len() >= arg.parse::<usize>().unwrap(), "min_length" => split_command.len() >= arg.parse::<usize>().unwrap(),
"length" => split_command.len() == arg.parse::<usize>().unwrap(), "length" => split_command.len() == arg.parse::<usize>().unwrap(),
"max_length" => split_command.len() <= arg.parse::<usize>().unwrap() + 1, "max_length" => split_command.len() <= arg.parse::<usize>().unwrap() + 1,

View file

@ -105,6 +105,7 @@ fn gen_match_rules(rules: &[Rule]) -> TokenStream {
for pattern in #patterns_tokens { for pattern in #patterns_tokens {
if error_msg.contains(pattern) { if error_msg.contains(pattern) {
#suggestion_tokens; #suggestion_tokens;
break;
}; };
})* })*
}) })
@ -149,7 +150,14 @@ fn parse_conditions(suggest: &str) -> (String, Vec<TokenStream2>) {
for condition in conditions { for condition in conditions {
let (mut condition, arg) = condition.split_once('(').unwrap(); let (mut condition, arg) = condition.split_once('(').unwrap();
condition = condition.trim(); condition = condition.trim();
let arg = arg.trim_start_matches('(').trim_end_matches(')'); // remove only the last character which is ')'
// other ')' are kept for regex
let arg = arg
.to_string()
.chars()
.take(arg.len() - 1)
.collect::<String>();
let reverse = match condition.starts_with('!') { let reverse = match condition.starts_with('!') {
true => { true => {
condition = condition.trim_start_matches('!'); condition = condition.trim_start_matches('!');
@ -157,7 +165,7 @@ fn parse_conditions(suggest: &str) -> (String, Vec<TokenStream2>) {
} }
false => false, false => false,
}; };
let evaluated_condition = eval_condition(condition, arg); let evaluated_condition = eval_condition(condition, &arg);
eval_conditions.push(quote! {#evaluated_condition == !#reverse}); eval_conditions.push(quote! {#evaluated_condition == !#reverse});
} }
@ -170,9 +178,8 @@ fn parse_conditions(suggest: &str) -> (String, Vec<TokenStream2>) {
fn eval_condition(condition: &str, arg: &str) -> TokenStream2 { fn eval_condition(condition: &str, arg: &str) -> TokenStream2 {
match condition { match condition {
"executable" => quote! {executables.contains(&#arg.to_string())}, "executable" => quote! {executables.contains(&#arg.to_string())},
"err_contains" => quote! {error_msg.contains(#arg)}, "err_contains" => quote! {regex_match(#arg, &error_msg)},
"cmd_contains" => quote! {last_command.contains(#arg)}, "cmd_contains" => quote! {regex_match(#arg, &last_command)},
"exe_contains" => quote! {split[0].contains(#arg)},
"min_length" => quote! {(split.len() >= #arg.parse::<usize>().unwrap())}, "min_length" => quote! {(split.len() >= #arg.parse::<usize>().unwrap())},
"length" => quote! {(split.len() == #arg.parse::<usize>().unwrap())}, "length" => quote! {(split.len() == #arg.parse::<usize>().unwrap())},
"max_length" => quote! {(split.len() <= #arg.parse::<usize>().unwrap() + 1)}, "max_length" => quote! {(split.len() <= #arg.parse::<usize>().unwrap() + 1)},

View file

@ -19,7 +19,7 @@ pattern = [
] ]
suggest = [ suggest = [
''' '''
#[exe_contains(/)] #[cmd_contains((?m)^(\S*)\/(\S*))]
chmod +x {{command[0]}} && chmod +x {{command[0]}} &&
{{command}}''' {{command}}'''
] ]

View file

@ -18,6 +18,11 @@ fn regex_captures(regex: &str, string: &str) -> Vec<String> {
caps caps
} }
pub fn regex_match(regex: &str, string: &str) -> bool {
let regex = Regex::new(regex).unwrap();
regex.is_match(string)
}
pub fn opt_regex(regex: &str, command: &mut String) -> String { pub fn opt_regex(regex: &str, command: &mut String) -> String {
let opts = regex_captures(regex, command); let opts = regex_captures(regex, command);