From 1b7b11bd3fee6d5e4cb05572ad5112ff6780a07b Mon Sep 17 00:00:00 2001 From: iff Date: Mon, 31 Jul 2023 14:12:45 +0200 Subject: [PATCH] BREAKING: use environments to get informations --- Cargo.toml | 2 +- README.md | 4 +- src/args.rs | 38 +++++++++++++ src/corrections.rs | 5 +- src/main.rs | 8 ++- src/shell.rs | 138 +++++++++++++++++++++++++-------------------- 6 files changed, 128 insertions(+), 67 deletions(-) create mode 100644 src/args.rs diff --git a/Cargo.toml b/Cargo.toml index d96cbb9..003c552 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pay_respect" -version = "0.1.0" +version = "0.2.0" edition = "2021" [dependencies] diff --git a/README.md b/README.md index 7d8797a..1947e0b 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,12 @@ The binary is named `pay-respect`, by adding an alias to your shell configuration: ``` shell # Note: You may need to have the binary exposed in your path -alias f="pay_respect" +alias f="$(pay_respect )" ``` You can now **press `F` to Pay Respect**! +Currently, only corrections to `bash`, `zsh`, and `fish` are implemented. + ## Installing If you are using Arch Linux, you can install from AUR directly: diff --git a/src/args.rs b/src/args.rs new file mode 100644 index 0000000..94ef710 --- /dev/null +++ b/src/args.rs @@ -0,0 +1,38 @@ +pub fn handle_args() { + let args = std::env::args().collect::>(); + if args.len() > 1 { + let shell = &args[1]; + let binary_path = &args[0]; + let last_command; + let alias; + + match shell.as_str() { + "bash" => { + last_command = "$(history 2)"; + alias = "$(alias)" + } + "zsh" => { + last_command = "$(fc -ln -1)"; + alias = "$(alias)" + } + "fish" => { + last_command = "$(history | head -n 1)"; + alias = "$(alias)"; + } + _ => { + println!("Unknown shell: {}", shell); + std::process::exit(1); + } + } + + println!( + "\ + _PR_LAST_COMMAND=\"{}\" \ + _PR_ALIAS=\"{}\" \ + _PR_SHELL=\"{}\" \ + \"{}\"", + last_command, alias, shell, binary_path + ); + std::process::exit(0); + } +} diff --git a/src/corrections.rs b/src/corrections.rs index d64d5e1..8f9c7d3 100644 --- a/src/corrections.rs +++ b/src/corrections.rs @@ -139,9 +139,10 @@ pub fn confirm_correction(shell: &str, command: &str, last_command: &str) { std::io::stdin().read_line(&mut String::new()).unwrap(); for p in PRIVILEGE_LIST { - if command.starts_with(p) { + let _p = p.to_owned() + " "; + if command.starts_with(&_p) { let command = command.replace(p, ""); - std::process::Command::new(p.trim()) + std::process::Command::new(p) .arg(shell) .arg("-c") .arg(command) diff --git a/src/main.rs b/src/main.rs index 3a09872..81d12da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,16 @@ +mod args; mod corrections; mod shell; mod style; fn main() { std::env::set_var("LC_ALL", "C"); + args::handle_args(); - let shell = shell::find_shell(); - let last_command = shell::find_last_command(&shell); + let shell = std::env::var("_PR_SHELL").expect( + "No _PR_SHELL in environment. Did you aliased the binary with the correct arguments?", + ); + let last_command = shell::last_command_expanded_alias(&shell); let corrected_command = corrections::correct_command(&shell, &last_command); if let Some(corrected_command) = corrected_command { diff --git a/src/shell.rs b/src/shell.rs index a3843f2..7f1f448 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1,56 +1,6 @@ -use std::{collections::HashMap, fs::read_to_string, process::exit}; +use std::{process::exit}; -pub const PRIVILEGE_LIST: [&str; 2] = ["doas ", "sudo "]; - -pub fn find_shell() -> String { - std::env::var("SHELL") - .unwrap_or_else(|_| String::from("bash")) - .rsplit('/') - .next() - .unwrap() - .to_string() - .to_lowercase() -} - -pub fn find_last_command(shell: &str) -> String { - let history_env = std::env::var("HISTFILE"); - let history_file = match history_env { - Ok(file) => file, - Err(_) => shell_default_history_file(shell), - }; - - let history = read_to_string(history_file) - .expect("Could not read history file. Try setting the HISTFILE environment variable."); - - match shell { - "bash" => history.lines().rev().nth(1).unwrap().to_string(), - "zsh" => history - .lines() - .rev() - .nth(1) - .unwrap() - .split_once(';') - .unwrap() - .1 - .to_string(), - "fish" => { - let mut history_lines = history.lines().rev(); - let mut last_command = String::new(); - let mut skips = 0; - while skips <= 2 { - last_command = history_lines.next().unwrap().to_string(); - if last_command.starts_with("- cmd") { - skips += 1; - } - } - last_command.split_once(": ").unwrap().1.to_string() - } - _ => { - println!("Unsupported shell."); - exit(1); - } - } -} +pub const PRIVILEGE_LIST: [&str; 2] = ["sudo", "doas"]; pub fn command_output(shell: &str, command: &str) -> String { let output = std::process::Command::new(shell) @@ -67,13 +17,79 @@ pub fn command_output(shell: &str, command: &str) -> String { .to_lowercase() } -fn shell_default_history_file(shell: &str) -> String { - let shell_file_map = HashMap::from([ - ("bash", String::from(".bash_history")), - ("zsh", String::from(".zsh_history")), - ("fish", String::from(".local/share/fish/fish_history")), - ]); - - let file = shell_file_map.get(shell).expect("Unsupported shell."); - format!("{}/{}", std::env::var("HOME").unwrap(), file) +fn last_command(shell: &str) -> String { + let last_command = std::env::var("_PR_LAST_COMMAND").expect("No _PR_LAST_COMMAND in environment. Did you aliased the command with the correct argument?"); + println!("last_command: {}", last_command); + match shell { + "bash" => { + let first_line = last_command.lines().next().unwrap(); + let split = first_line.split_whitespace().collect::>(); + split[1..].join(" ") + } + "zsh" => last_command, + "fish" => last_command, + _ => { + eprintln!("Unsupported shell: {}", shell); + exit(1); + } + } +} + +pub fn last_command_expanded_alias(shell: &str) -> String { + let alias = std::env::var("_PR_ALIAS").expect( + "No _PR_ALIAS in environment. Did you aliased the command with the correct argument?", + ); + let last_command = last_command(shell); + if alias.is_empty() { + return last_command; + } + + let split_command = last_command.split_whitespace().collect::>(); + let command; + if PRIVILEGE_LIST.contains(&split_command[0]) { + command = split_command[1]; + } else { + command = split_command[0]; + } + + let mut expanded_command = command.to_string(); + + match shell { + "bash" => { + for line in alias.lines() { + if line.starts_with(format!("alias {}=", command).as_str()) { + let alias = line.replace(format!("alias {}='", command).as_str(), ""); + let alias = alias.trim_end_matches('\'').trim_start_matches('\''); + + expanded_command = alias.to_string(); + } + } + } + "zsh" => { + for line in alias.lines() { + if line.starts_with(format!("{}=", command).as_str()) { + let alias = line.replace(format!("{}=", command).as_str(), ""); + let alias = alias.trim_start_matches('\'').trim_end_matches('\''); + + expanded_command = alias.to_string(); + } + } + } + "fish" => { + for line in alias.lines() { + if line.starts_with(format!("alias {} ", command).as_str()) { + let alias = line.replace(format!("alias {} ", command).as_str(), ""); + let alias = alias.trim_start_matches('\'').trim_end_matches('\''); + + expanded_command = alias.to_string(); + } + } + } + _ => { + eprintln!("Unsupported shell: {}", shell); + exit(1); + } + }; + + last_command.replacen(command, &expanded_command, 1) }