chore: rearrange directories

This commit is contained in:
iff 2024-12-08 16:52:53 +01:00
parent d5fb7462e0
commit 4c9aac45a8
46 changed files with 11 additions and 18 deletions

View file

@ -0,0 +1,11 @@
[package]
name = "pay-respects-module-runtime-rules"
version = "0.1.0"
edition = "2021"
[dependencies]
regex-lite = "0.1"
toml = { version = "0.8" }
serde = { version = "1.0", features = ["derive"] }
pay-respects-utils = {version = "0.1.0", path = "../utils"}

View file

@ -0,0 +1,25 @@
mod replaces;
mod rules;
fn main() -> Result<(), std::io::Error> {
let executable = std::env::var("_PR_COMMAND").expect("_PR_COMMAND not set");
let shell = std::env::var("_PR_SHELL").expect("_PR_SHELL not set");
let last_command = std::env::var("_PR_LAST_COMMAND").expect("_PR_LAST_COMMAND not set");
let error_msg = std::env::var("_PR_ERROR_MSG").expect("_PR_ERROR_MSG not set");
let executables: Vec<String> = {
let executables = std::env::var("_PR_EXECUTABLES").expect("_PR_EXECUTABLES not set");
executables.split(",").map(|s| s.to_string()).collect()
};
#[cfg(debug_assertions)]
{
eprintln!("shell: {}", shell);
eprintln!("executable: {}", executable);
eprintln!("last_command: {}", last_command);
eprintln!("error_msg: {}", error_msg);
eprintln!("executables: {:?}", executables);
}
rules::runtime_match(&executable, &shell, &last_command, &error_msg, &executables);
Ok(())
}

View file

@ -0,0 +1,185 @@
use pay_respects_utils::evals::*;
fn tag(name: &str, x: i32) -> String {
format!("{{{}{}}}", name, x)
}
pub fn eval_placeholder(
string: &str,
start: &str,
end: &str,
) -> (std::ops::Range<usize>, std::ops::Range<usize>) {
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, last_command: &mut String, opt_list: &mut Vec<(String, String)>) {
let mut replace_tag = 0;
let tag_name = "opts";
while suggest.contains(" {{opt::") {
let (placeholder, args) = eval_placeholder(suggest, " {{opt::", "}}");
let opt = &suggest[args.to_owned()];
let regex = opt.trim();
let current_tag = tag(tag_name, replace_tag);
opt_list.push((current_tag.clone(), opt_regex(regex, last_command)));
suggest.replace_range(placeholder, &current_tag);
replace_tag += 1;
}
}
pub fn cmd_reg(suggest: &mut String, last_command: &str) {
while suggest.contains("{{cmd::") {
let (placeholder, args) = eval_placeholder(suggest, "{{cmd::", "}}");
let regex = suggest[args.to_owned()].trim();
let command = cmd_regex(regex, last_command);
suggest.replace_range(placeholder, &command)
}
}
pub fn err(suggest: &mut String, error_msg: &str) {
while suggest.contains("{{err::") {
let (placeholder, args) = eval_placeholder(suggest, "{{err::", "}}");
let regex = suggest[args.to_owned()].trim();
let command = err_regex(regex, error_msg);
suggest.replace_range(placeholder, &command)
}
}
pub fn command(suggest: &mut String, split_command: &[String]) {
while suggest.contains("{{command") {
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_index = start.parse::<i32>().unwrap_or(0);
if start_index < 0 {
start_index += split_command.len() as i32;
};
let mut end_index;
let parsed_end = end.parse::<i32>();
if parsed_end.is_err() {
end_index = split_command.len() as i32;
} else {
end_index = parsed_end.unwrap();
if end_index < 0 {
end_index += split_command.len() as i32 + 1;
} else {
end_index += 1;
}
};
let command = split_command[start_index as usize..end_index as usize].join(" ");
suggest.replace_range(placeholder, &command);
} else {
let range = range.parse::<usize>().unwrap_or(0);
let command = &split_command[range];
suggest.replace_range(placeholder, command);
}
}
}
pub fn typo(suggest: &mut String, split_command: &[String], executables: &[String], shell: &str) {
while suggest.contains("{{typo") {
let (placeholder, args) = eval_placeholder(suggest, "{{typo", "}}");
let index = if suggest.contains('[') {
let split = suggest[args.to_owned()]
.split(&['[', ']'])
.collect::<Vec<&str>>();
let command_index = split[1];
if !command_index.contains(':') {
let command_index = command_index.parse::<i32>().unwrap();
let index = if command_index < 0 {
split_command.len() as i32 + command_index
} else {
command_index
};
index as usize..index as usize + 1
} else {
let (start, end) = command_index.split_once(':').unwrap();
let start = start.parse::<i32>().unwrap_or(0);
let start_index = if start < 0 {
split_command.len() as i32 + start
} else {
start
};
let end = end.parse::<i32>();
let end_index = if end.is_err() {
split_command.len() as i32
} else {
let end = end.unwrap();
if end < 0 {
split_command.len() as i32 + end + 1
} else {
end + 1
}
};
start_index as usize..end_index as usize
}
} else {
unreachable!("Typo suggestion must have a command index");
};
let match_list = if suggest.contains('(') {
let split = suggest[args.to_owned()]
.split_once("(")
.unwrap()
.1
.rsplit_once(")")
.unwrap()
.0;
split.split(',').collect::<Vec<&str>>()
} else {
unreachable!("Typo suggestion must have a match list");
};
let match_list = match_list
.iter()
.map(|s| s.trim().to_string())
.collect::<Vec<String>>();
let command = if match_list[0].starts_with("{{shell") {
let function = match_list.join(",");
let (_, args) = eval_placeholder(&function, "{{shell", "}}");
let function = &function[args.to_owned()].trim_matches(|c| c == '(' || c == ')');
suggest_typo(
&split_command[index],
eval_shell_command(shell, function),
executables,
)
} else {
suggest_typo(&split_command[index], match_list, executables)
};
suggest.replace_range(placeholder, &command);
}
}
pub fn shell(suggest: &mut String, shell: &str) {
while suggest.contains("{{shell") {
let (placeholder, args) = eval_placeholder(suggest, "{{shell", "}}");
let range = suggest[args.to_owned()].trim_matches(|c| c == '(' || c == ')');
let command = eval_shell_command(shell, range);
suggest.replace_range(placeholder, &command.join("\n"));
}
}

View file

@ -0,0 +1,191 @@
use crate::replaces;
use pay_respects_utils::evals::*;
#[derive(serde::Deserialize)]
struct Rule {
match_err: Vec<MatchError>,
}
#[derive(serde::Deserialize)]
struct MatchError {
pattern: Vec<String>,
suggest: Vec<String>,
}
pub fn runtime_match(
executable: &str,
shell: &str,
last_command: &str,
error_msg: &str,
executables: &[String],
) {
let file = get_rule(executable);
if file.is_none() {
return;
}
let file = std::fs::read_to_string(file.unwrap()).unwrap();
let rule: Rule = toml::from_str(&file).unwrap();
let split_command = split_command(last_command);
let mut pure_suggest;
for match_err in rule.match_err {
for pattern in match_err.pattern {
if error_msg.contains(&pattern) {
'suggest: for suggest in &match_err.suggest {
if suggest.starts_with('#') {
let mut lines = suggest.lines().collect::<Vec<&str>>();
let mut conditions = String::new();
for (i, line) in lines[0..].iter().enumerate() {
conditions.push_str(line);
if line.ends_with(']') {
lines = lines[i + 1..].to_vec();
break;
}
}
let conditions = conditions
.trim_start_matches(['#', '['])
.trim_end_matches(']')
.split(',')
.collect::<Vec<&str>>();
for condition in conditions {
let (mut condition, arg) = condition.split_once('(').unwrap();
condition = condition.trim();
let arg = arg.trim_start_matches('(').trim_end_matches(')');
let reverse = match condition.starts_with('!') {
true => {
condition = condition.trim_start_matches('!');
true
}
false => false,
};
if eval_condition(
condition,
arg,
shell,
last_command,
error_msg,
&split_command,
executables,
) == reverse
{
continue 'suggest;
}
}
pure_suggest = lines.join("\n").to_owned();
} else {
pure_suggest = suggest.to_owned();
}
// replacing placeholders
if pure_suggest.contains("{{command}}") {
pure_suggest = pure_suggest.replace("{{command}}", last_command);
}
print!(
"{}",
eval_suggest(&pure_suggest, last_command, error_msg, executables, shell,)
);
print!("<_PR_BR>");
}
}
}
}
}
fn eval_condition(
condition: &str,
arg: &str,
shell: &str,
last_command: &str,
error_msg: &str,
split_command: &[String],
executables: &[String],
) -> bool {
match condition {
"executable" => executables.contains(&arg.to_string()),
"err_contains" => error_msg.contains(arg),
"cmd_contains" => last_command.contains(arg),
"min_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,
"shell" => shell == arg,
_ => unreachable!("Unknown condition when evaluation condition: {}", condition),
}
}
fn eval_suggest(
suggest: &str,
last_command: &str,
error_msg: &str,
executables: &[String],
shell: &str,
) -> String {
let mut suggest = suggest.to_owned();
if suggest.contains("{{command}}") {
suggest = suggest.replace("{{command}}", "{last_command}");
}
let mut last_command = last_command.to_owned();
let mut opt_list = Vec::new();
replaces::opts(&mut suggest, &mut last_command, &mut opt_list);
let split_command = split_command(&last_command);
replaces::cmd_reg(&mut suggest, &last_command);
replaces::err(&mut suggest, error_msg);
replaces::command(&mut suggest, &split_command);
replaces::shell(&mut suggest, shell);
replaces::typo(&mut suggest, &split_command, executables, shell);
for (tag, value) in opt_list {
suggest = suggest.replace(&tag, &value);
}
suggest
}
fn get_rule(executable: &str) -> Option<String> {
let xdg_config_home = if cfg!(windows) {
std::env::var("APPDATA").unwrap()
} else {
std::env::var("XDG_CONFIG_HOME")
.unwrap_or_else(|_| std::env::var("HOME").unwrap() + "/.config")
};
let user_rule_dir = format!("{}/pay-respects/rules", xdg_config_home);
let user_rule_file = format!("{}/{}.toml", user_rule_dir, executable);
if std::path::Path::new(&user_rule_file).exists() {
return Some(user_rule_file);
}
let check_dirs = |dirs: Vec<&str>| -> Option<String> {
for dir in dirs {
let rule_dir = format!("{}/pay-respects/rules", dir);
let rule_file = format!("{}/{}.toml", rule_dir, executable);
if std::path::Path::new(&rule_file).exists() {
return Some(rule_file);
}
}
None
};
let xdg_config_dirs = std::env::var("XDG_CONFIG_DIRS").unwrap_or("/etc/xdg".to_owned());
let xdg_config_dirs = xdg_config_dirs.split(':').collect::<Vec<&str>>();
if let Some(file) = check_dirs(xdg_config_dirs) {
return Some(file);
}
let xdg_data_dirs =
std::env::var("XDG_DATA_DIRS").unwrap_or("/usr/local/share:/usr/share".to_owned());
let xdg_data_dirs = xdg_data_dirs.split(':').collect::<Vec<&str>>();
if let Some(file) = check_dirs(xdg_data_dirs) {
return Some(file);
}
None
}