use std::collections::{HashMap, HashSet};
use std::process::Stdio;
use std::sync::{Arc, Mutex};
use std::time::Duration;
pub fn load_aliases_from_shell(timeout: Duration) -> HashMap<String, String> {
let shell = std::env::var("SHELL").unwrap_or_else(|_| "sh".into());
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let result = std::process::Command::new(&shell)
.arg("-i")
.arg("-c")
.arg("alias")
.stdout(Stdio::piped())
.stderr(Stdio::null()) .spawn();
match result {
Ok(child) => {
let result = child.wait_with_output();
let _ = tx.send(result);
}
Err(e) => {
eprintln!("Failed to spawn shell for aliases: {}", e);
}
}
});
if let Ok(Ok(output)) = rx.recv_timeout(timeout) {
parse_aliases(&String::from_utf8_lossy(&output.stdout))
} else {
eprintln!("Timeout or error loading aliases from shell.");
HashMap::new()
}
}
fn parse_aliases(output: &str) -> HashMap<String, String> {
let mut aliases = HashMap::new();
for line in output.lines() {
let line = line.trim();
let content = line.strip_prefix("alias ").unwrap_or(line);
if let Some((name, value)) = content.split_once('=') {
let name = name.trim();
let value = value.trim();
let value = if value.len() >= 2
&& ((value.starts_with('\'') && value.ends_with('\''))
|| (value.starts_with('"') && value.ends_with('"')))
{
&value[1..value.len() - 1]
} else {
value
};
if !name.contains(char::is_whitespace) && !name.is_empty() {
aliases.insert(name.to_string(), value.to_string());
}
}
}
aliases
}
pub fn expand_alias(input: &str, aliases_lock: &Arc<Mutex<HashMap<String, String>>>) -> String {
let mut current_input = input.to_string();
let mut expanded_cmds = HashSet::new();
let aliases = match aliases_lock.lock() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
};
for _ in 0..10 {
let input_clone = current_input.clone();
let trimmed = input_clone.trim_start();
let first_word_end = trimmed.find(char::is_whitespace).unwrap_or(trimmed.len());
let first_word = &trimmed[..first_word_end];
if first_word.is_empty() {
break;
}
if let Some(replacement) = aliases.get(first_word) {
if expanded_cmds.contains(first_word) {
break;
}
expanded_cmds.insert(first_word.to_string());
let rest = &trimmed[first_word_end..];
current_input = format!("{}{}", replacement, rest);
} else {
break;
}
}
current_input
}