pay-respects/src/shell.rs

269 lines
5.9 KiB
Rust
Raw Normal View History

2023-08-03 21:26:41 +02:00
use std::process::exit;
2023-07-30 18:40:18 +02:00
use std::sync::mpsc::channel;
use std::thread;
use std::time::Duration;
pub const PRIVILEGE_LIST: [&str; 2] = ["sudo", "doas"];
2023-07-30 18:40:18 +02:00
pub fn command_output(shell: &str, command: &str) -> String {
let (sender, receiver) = channel();
let _shell = shell.to_owned();
let _command = command.to_owned();
thread::spawn(move || {
2023-08-07 20:21:02 +02:00
sender
.send(
std::process::Command::new(_shell)
.arg("-c")
.arg(_command)
.env("LC_ALL", "C")
.output()
.expect("failed to execute process"),
)
.expect("failed to send output");
});
match receiver.recv_timeout(Duration::from_secs(3)) {
Ok(output) => match output.stderr.is_empty() {
true => String::from_utf8_lossy(&output.stdout).to_lowercase(),
false => String::from_utf8_lossy(&output.stderr).to_lowercase(),
2024-10-19 16:48:34 +02:00
},
Err(_) => {
use colored::*;
eprintln!("Timeout while executing command: {}", command.red());
exit(1);
}
}
2023-07-30 18:40:18 +02:00
}
2024-11-16 00:25:28 +01:00
pub fn last_command(shell: &str) -> String {
let last_command = match std::env::var("_PR_LAST_COMMAND") {
Ok(command) => command,
Err(_) => {
2024-09-25 17:55:55 +02:00
eprintln!(
"{}",
t!(
"no-env-setup",
var = "_PR_LAST_COMMAND",
help = "pay-respects -h"
)
);
exit(1);
}
};
match shell {
"bash" => {
2023-08-06 11:20:43 +02:00
let first_line = last_command.lines().next().unwrap().trim();
2023-08-07 20:21:02 +02:00
first_line.split_once(' ').unwrap().1.to_string()
}
"zsh" => last_command,
"fish" => last_command,
2023-07-31 15:06:30 +02:00
"nu" => last_command,
_ => {
eprintln!("Unsupported shell: {}", shell);
exit(1);
}
}
}
2024-11-16 00:25:28 +01:00
pub fn expand_alias(shell: &str, full_command: &str) -> String {
2024-09-25 17:55:55 +02:00
let alias = std::env::var("_PR_ALIAS").expect(&t!(
"no-env-setup",
var = "_PR_ALIAS",
help = "pay-respects -h"
));
if alias.is_empty() {
2024-11-16 00:25:28 +01:00
return full_command.to_string();
}
2024-11-16 00:25:28 +01:00
let split_command = full_command.split_whitespace().collect::<Vec<&str>>();
2024-11-17 14:57:22 +01:00
let (command, pure_command) = if PRIVILEGE_LIST.contains(&split_command[0]) {
(split_command[1], Some(split_command[1..].join(" ")))
} else {
2024-11-17 14:57:22 +01:00
(split_command[0], None)
2023-07-31 18:46:35 +02:00
};
2024-11-16 00:25:28 +01:00
let mut expanded_command = Option::None;
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('\'');
2024-11-16 00:25:28 +01:00
expanded_command = Some(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('\'');
2024-11-16 00:25:28 +01:00
expanded_command = Some(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('\'');
2024-11-16 00:25:28 +01:00
expanded_command = Some(alias.to_string());
}
}
}
_ => {
eprintln!("Unsupported shell: {}", shell);
exit(1);
}
};
2023-07-30 18:40:18 +02:00
2024-11-16 00:25:28 +01:00
if expanded_command.is_none() {
2024-11-17 14:57:22 +01:00
return full_command.to_string();
};
let expanded_command = expanded_command.unwrap();
if pure_command.is_some() {
let pure_command = pure_command.unwrap();
if pure_command.starts_with(&expanded_command) {
return full_command.to_string();
}
}
full_command.replacen(command, &expanded_command, 1)
}
pub fn expand_alias_multiline(shell: &str, full_command: &str) -> String {
let lines = full_command.lines().collect::<Vec<&str>>();
let mut expanded = String::new();
for line in lines {
expanded = format!("{}\n{}", expanded, expand_alias(shell, line));
2024-11-16 00:25:28 +01:00
}
2024-11-17 14:57:22 +01:00
expanded
2023-07-30 18:40:18 +02:00
}
2023-08-04 15:40:45 +02:00
pub fn initialization(shell: &str, binary_path: &str, auto_alias: &str) {
2023-08-03 21:26:41 +02:00
let last_command;
let alias;
2023-08-03 21:26:41 +02:00
match shell {
"bash" => {
last_command = "$(history 2)";
alias = "$(alias)"
}
"zsh" => {
last_command = "$(fc -ln -1)";
alias = "$(alias)"
}
"fish" => {
last_command = "$(history | head -n 1)";
alias = "$(alias)";
}
"nu" | "nush" | "nushell" => {
last_command = "(history | last).command";
alias = "\"\"";
}
2023-08-03 21:26:41 +02:00
_ => {
println!("Unknown shell: {}", shell);
std::process::exit(1);
}
}
2024-11-18 13:53:16 +01:00
if shell == "nu" || shell == "nush" || shell == "nushell"{
let pr_alias = if auto_alias.is_empty() {
"f"
} else {
auto_alias
};
let init = format!(
r#"def --env {} [] {{
let dir = (with-env {{ _PR_LAST_COMMAND: {}, _PR_ALIAS: {}, _PR_SHELL: nu }} {{ {} }})
cd $dir
}}"#,
pr_alias, last_command, alias, binary_path
);
println!("{}", init);
std::process::exit(0);
}
2023-08-04 15:40:45 +02:00
let mut init = format!(
2023-08-03 21:26:41 +02:00
"\
eval $(_PR_LAST_COMMAND=\"{}\" \
_PR_ALIAS=\"{}\" \
_PR_SHELL=\"{}\" \
\"{}\")",
2023-08-03 21:26:41 +02:00
last_command, alias, shell, binary_path
);
2023-08-04 15:40:45 +02:00
if auto_alias.is_empty() {
println!("{}", init);
std::process::exit(0);
}
match shell {
"bash" | "zsh" => {
2023-08-12 23:00:23 +02:00
init = format!(r#"alias {}='{}'"#, auto_alias, init);
2023-08-04 15:40:45 +02:00
}
"fish" => {
init = format!(
r#"
function {} -d "Terminal command correction"
2023-08-12 23:09:56 +02:00
eval $({})
end
"#,
2023-08-04 15:40:45 +02:00
auto_alias, init
);
}
_ => {
println!("Unsupported shell: {}", shell);
exit(1);
}
}
println!("{}", init);
2023-08-03 21:26:41 +02:00
std::process::exit(0);
}
pub fn shell_syntax(shell: &str, command: &mut String) {
#[allow(clippy::single_match)]
match shell {
"nu" => {
*command = command.replace(" && ", " and ");
}
_ => {}
}
}
pub fn shell_evaluated_commands(shell: &str, command: &str) -> Option<String> {
let lines = command
.lines()
.map(|line| line.trim().trim_end_matches(['\\', ';', '|', '&']))
.collect::<Vec<&str>>();
let mut dirs = Vec::new();
for line in lines {
if let Some(dir) = line.strip_prefix("cd ") {
dirs.push(dir.to_string());
}
}
let cd_dir = dirs.join("");
if cd_dir.is_empty() {
return None;
}
#[allow(clippy::single_match)]
match shell {
"nu" => Some(cd_dir),
_ => Some(format!("cd {}", cd_dir)),
}
}