From 8ae71443d62a7dcc364d5ece41ee18b4650cb4cd Mon Sep 17 00:00:00 2001 From: iff Date: Fri, 4 Aug 2023 00:18:31 +0200 Subject: [PATCH] feat: get output from commandline --- rule_parser/src/lib.rs | 5 +- rule_parser/src/replaces.rs | 122 ++++++++++++++++++++++++------------ rules/git.toml | 11 ++++ src/suggestions.rs | 38 +++++++++-- 4 files changed, 132 insertions(+), 44 deletions(-) diff --git a/rule_parser/src/lib.rs b/rule_parser/src/lib.rs index 47dee97..dcdbaea 100644 --- a/rule_parser/src/lib.rs +++ b/rule_parser/src/lib.rs @@ -177,7 +177,7 @@ fn eval_condition(condition: &str, arg: &str) -> TokenStream2 { } }, "err_contains" => quote!{error_msg.contains(#arg)}, - "cmd_contains" => quote!{command.contains(#arg)}, + "cmd_contains" => quote!{last_command.contains(#arg)}, _ => unreachable!("Unknown condition when evaluation condition: {}", condition), } } @@ -190,10 +190,13 @@ fn eval_suggest(suggest: &str) -> TokenStream2 { let mut replace_list = Vec::new(); let mut opt_list = Vec::new(); + let mut cmd_list = Vec::new(); replaces::opts(&mut suggest, &mut replace_list, &mut opt_list); + replaces::shell(&mut suggest, &mut cmd_list); replaces::command(&mut suggest, &mut replace_list); replaces::typo(&mut suggest, &mut replace_list); + replaces::shell_tag(&mut suggest, &mut replace_list, cmd_list); quote! { #(#opt_list)* diff --git a/rule_parser/src/replaces.rs b/rule_parser/src/replaces.rs index ae85a5e..6975f2c 100644 --- a/rule_parser/src/replaces.rs +++ b/rule_parser/src/replaces.rs @@ -14,21 +14,25 @@ fn tag (name: &str, x: i32) -> String { tag } +fn eval_placeholder(string: &str, start: &str, end: &str) -> (std::ops::Range, std::ops::Range) { + let start_index = string.find(start).unwrap(); + let end_index = string[start_index..].find(end).unwrap() + + start_index + + end.len(); + + let placeholder = start_index..end_index; + + let args = start_index + start.len()..end_index - end.len(); + + (placeholder, args) +} + pub fn opts(suggest: &mut String, replace_list: &mut Vec, opt_list: &mut Vec) { let mut replace_tag = 0; let tag_name = "opts"; while suggest.contains("{{opt::") { - let placeholder_start = "{{opt::"; - let placeholder_end = "}}"; + let (placeholder, args) = eval_placeholder(suggest, "{{opt::", "}}"); - let start_index = suggest.find(placeholder_start).unwrap(); - let end_index = suggest[start_index..].find(placeholder_end).unwrap() - + start_index - + placeholder_end.len(); - - let placeholder = start_index..end_index; - - let args = start_index + placeholder_start.len()..end_index - placeholder_end.len(); let opt = &suggest[args.to_owned()]; let regex = opt.trim(); let current_tag = tag(tag_name, replace_tag); @@ -48,17 +52,8 @@ pub fn command(suggest: &mut String, replace_list: &mut Vec) { let mut replace_tag = 0; let tag_name = "command"; while suggest.contains("{{command") { - let placeholder_start = "{{command"; - let placeholder_end = "}}"; - - let start_index = suggest.find(placeholder_start).unwrap(); - let end_index = suggest[start_index..].find(placeholder_end).unwrap() - + start_index - + placeholder_end.len(); - - let placeholder = start_index..end_index; - - let args = start_index + placeholder_start.len()..end_index - placeholder_end.len(); + let (placeholder, args) = eval_placeholder(suggest, "{{command", "}}"); + let range = suggest[args.to_owned()].trim_matches(|c| c == '[' || c == ']'); if let Some((start, end)) = range.split_once(':') { let mut start_string = start.to_string(); @@ -100,16 +95,7 @@ pub fn typo(suggest: &mut String, replace_list: &mut Vec) { let tag_name = "typo"; while suggest.contains("{{typo") { - let placeholder_start = "{{typo"; - let placeholder_end = "}}"; - - let start_index = suggest.find(placeholder_start).unwrap(); - let end_index = suggest[start_index..].find(placeholder_end).unwrap() - + start_index - + placeholder_end.len(); - - let placeholder = start_index..end_index; - let args = start_index + placeholder_start.len()..end_index - placeholder_end.len(); + let (placeholder, args) = eval_placeholder(suggest, "{{typo", "}}"); let string_index; if suggest.contains('[') { @@ -118,7 +104,6 @@ pub fn typo(suggest: &mut String, replace_list: &mut Vec) { .collect::>(); let command_index = split[1].parse::().unwrap(); if command_index < 0 { - // command_index += split_command.len() as i32; string_index = format!("split_command.len() {}", command_index); } else { string_index = command_index.to_string(); @@ -126,22 +111,38 @@ pub fn typo(suggest: &mut String, replace_list: &mut Vec) { } else { unreachable!("Typo suggestion must have a command index"); } - let mut match_list = Vec::new(); + let match_list; if suggest.contains('(') { let split = suggest[args.to_owned()] - .split(&['(', ')']) - .collect::>(); - match_list = split[1].trim().split(',').collect::>(); + .split_once("(").unwrap().1.rsplit_once(")").unwrap().0; + match_list = split.split(',').collect::>(); + } else { + unreachable!("Typo suggestion must have a match list"); } let match_list = match_list .iter() .map(|s| s.trim().to_string()) .collect::>(); - let string_match_list = match_list.join(r#"".to_string(), ""#); - let string_match_list = format!(r#""{}".to_string()"#, string_match_list); - let command = format!("suggest_typo(&split_command[{}], vec![{}])", string_index, string_match_list); + let command; + if match_list[0].starts_with("eval_shell_command("){ + let function = match_list.join(","); + // add a " after first comma, and a " before last ) + let function = format!("{}\"{}{}", + &function[..function.find(',').unwrap() + 1], + &function[function.find(',').unwrap() + 1..function.len() - 1], "\")"); + command = format!("suggest_typo(&split_command[{}], {})", string_index, function); + } else { + let match_list = match_list + .iter() + .map(|s| s.trim().to_string()) + .collect::>(); + let string_match_list = match_list.join("\".to_string(), \""); + let string_match_list = format!("\"{}\".to_string()", string_match_list); + command = format!("suggest_typo(&split_command[{}], vec![{}])", string_index, string_match_list); + } + replace_list.push(rtag(tag_name, replace_tag, command)); suggest.replace_range(placeholder, &tag(tag_name, replace_tag)); @@ -149,3 +150,46 @@ pub fn typo(suggest: &mut String, replace_list: &mut Vec) { } } + +pub fn err(suggest: &mut String, replace_list: &mut Vec) { + let mut replace_tag = 0; + let tag_name = "err"; + + while suggest.contains("{{err::") { + let (placeholder, args) = eval_placeholder(suggest, "{{err::", "}}"); + + let regex = suggest[args.to_owned()].trim(); + + let command = format!("opt_regex({}, &mut error_msg)", regex); + + replace_list.push(rtag(tag_name, replace_tag, command)); + suggest.replace_range(placeholder, &tag(tag_name, replace_tag)); + replace_tag += 1; + } +} + +pub fn shell(suggest: &mut String, cmd_list: &mut Vec) { + while suggest.contains("{{shell") { + let (placeholder, args) = eval_placeholder(suggest, "{{shell", "}}"); + let range = suggest[args.to_owned()].trim_matches(|c| c == '(' || c == ')'); + + let command = format!("eval_shell_command(shell, {})", range); + + suggest.replace_range(placeholder, &command); + cmd_list.push(command); + } +} + +pub fn shell_tag(suggest: &mut String, replace_list: &mut Vec, cmd_list: Vec) { + let mut replace_tag = 0; + let tag_name = "shell"; + + for command in cmd_list { + if suggest.contains(&command) { + + *suggest = suggest.replace(&command, &tag(tag_name, replace_tag)); + replace_list.push(rtag(tag_name, replace_tag, command)); + replace_tag += 1; + } + } +} diff --git a/rules/git.toml b/rules/git.toml index f60c745..b48e341 100644 --- a/rules/git.toml +++ b/rules/git.toml @@ -64,3 +64,14 @@ repack, replace, )}} {{command[2:]}}''' ] + +[[match_err]] +pattern = [ + "did not match any file" +] +suggest = [ +''' +#[cmd_contains(checkout)] +git checkout {{typo[2]({{shell(git branch)}})}} +''' +] diff --git a/src/suggestions.rs b/src/suggestions.rs index 9110f5b..7089eae 100644 --- a/src/suggestions.rs +++ b/src/suggestions.rs @@ -16,12 +16,12 @@ pub fn suggest_command(shell: &str, last_command: &str) -> Option { }; if !PRIVILEGE_LIST.contains(&executable) { - let suggest = match_pattern("privilege", last_command, &err); + let suggest = match_pattern("privilege", last_command, &err, shell); if suggest.is_some() { return suggest; } } - let suggest = match_pattern(executable, last_command, &err); + let suggest = match_pattern(executable, last_command, &err, shell); if let Some(suggest) = suggest { if PRIVILEGE_LIST.contains(&executable) { return Some(format!("{} {}", split_command[0], suggest)); @@ -29,14 +29,19 @@ pub fn suggest_command(shell: &str, last_command: &str) -> Option { return Some(suggest); } - let suggest = match_pattern("general", last_command, &err); + let suggest = match_pattern("general", last_command, &err, shell); if let Some(suggest) = suggest { return Some(suggest); } None } -fn match_pattern(executable: &str, last_command: &str, error_msg: &str) -> Option { +fn match_pattern( + executable: &str, + last_command: &str, + error_msg: &str, + shell: &str, +) -> Option { parse_rules!("rules"); } @@ -52,6 +57,31 @@ fn opt_regex(regex: &str, command: &mut String) -> String { opts.join(" ") } +fn err_regex(regex: &str, error_msg: &str) -> String { + let regex = Regex::new(regex).unwrap(); + let err = regex + .find_iter(error_msg) + .map(|cap| cap.as_str().to_owned()) + .collect::>(); + + err.join(" ") +} + +fn eval_shell_command(shell: &str, command: &str) -> Vec { + let output = std::process::Command::new(shell) + .arg("-c") + .arg(command) + .output() + .expect("failed to execute process"); + let output = String::from_utf8_lossy(&output.stdout); + let split_output = output.split('\n').collect::>(); + println!("{:?}", split_output); + split_output + .iter() + .map(|s| s.trim().to_string()) + .collect::>() +} + pub fn split_command(command: &str) -> Vec { let regex = r#"([^\s"\\]+|"(?:\\.|[^"\\])*"|\\.)+"#; let regex = Regex::new(regex).unwrap();