use crate::cli::cli_data::InitShell;
use std::path::PathBuf;
pub struct ShellOpts {
pub app_path: String,
pub data_dir: String,
pub config_dir: String,
pub alias_file_path: String,
}
impl ShellOpts {
pub fn new() -> Self {
let app_path = std::env::current_exe()
.unwrap_or_else(|_| PathBuf::from("alman"))
.to_string_lossy()
.to_string();
let data_dir = crate::database::persistence::get_data_directory()
.unwrap_or_else(|_| {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".local")
.join("share")
.join("alman")
})
.to_string_lossy()
.to_string();
let config_dir = crate::database::persistence::get_config_directory()
.unwrap_or_else(|_| {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".config")
.join("alman")
})
.to_string_lossy()
.to_string();
let alias_file_path = crate::database::persistence::load_config()
.and_then(|cfg| cfg.alias_file_paths.first().cloned())
.unwrap_or_else(|| crate::database::persistence::get_default_alias_file_path());
Self {
app_path,
data_dir,
config_dir,
alias_file_path,
}
}
}
pub fn render_shell_init(shell: InitShell, opts: &ShellOpts) -> String {
match shell {
InitShell::Bash => render_bash(opts),
InitShell::Zsh => render_zsh(opts),
InitShell::Fish => render_fish(opts),
InitShell::Posix => render_posix(opts),
}
}
fn render_bash(opts: &ShellOpts) -> String {
let mut script = String::new();
script.push_str("# Alman shell integration for bash\n");
script.push_str("# Add this to your ~/.bashrc\n\n");
script.push_str(&format!("export ALMAN_DATA_DIR=\"{}\"\n", opts.data_dir));
script.push_str(&format!("export ALMAN_CONFIG_DIR=\"{}\"\n", opts.config_dir));
script.push_str(&format!("export ALMAN_BIN=\"{}\"\n\n", opts.app_path));
script.push_str("alman_preexec() {\n");
script.push_str(" # Skip if no command or if it's an alman internal command\n");
script.push_str(" if [ -n \"$1\" ] && [[ \"$1\" != alman_source_aliases* ]] && [[ \"$1\" != alman_preexec* ]] && [[ \"$1\" != trap* ]]; then\n");
script.push_str(&format!(" {} custom \"$1\" >/dev/null 2>&1\n", opts.app_path));
script.push_str(" fi\n");
script.push_str("}\n\n");
script.push_str("alman_source_aliases() {\n");
script.push_str(" # Source all alias files from alman config\n");
script.push_str(&format!(" if [ -f \"{}/config.json\" ]; then\n", opts.config_dir));
script.push_str(" # Extract alias file paths from config and source them\n");
script.push_str(" local alias_files=$(cat \"$ALMAN_CONFIG_DIR/config.json\" | grep -o '\"[^\"]*aliases[^\"]*\"' | tr -d '\"')\n");
script.push_str(" for file in $alias_files; do\n");
script.push_str(" if [ -f \"$file\" ]; then\n");
script.push_str(" source \"$file\"\n");
script.push_str(" fi\n");
script.push_str(" done\n");
script.push_str(" else\n");
script.push_str(&format!(" # Fallback to default alias file\n"));
script.push_str(&format!(" if [ -f \"{}\" ]; then\n", opts.alias_file_path));
script.push_str(&format!(" source \"{}\"\n", opts.alias_file_path));
script.push_str(" fi\n");
script.push_str(" fi\n");
script.push_str("}\n\n");
script.push_str("# Initialize alman data directory and files\n");
script.push_str(&format!("{} init-data >/dev/null 2>&1\n", opts.app_path));
script.push_str("\n");
script.push_str("# Set up command tracking for interactive bash shells\n");
script.push_str("if [ -n \"$BASH_VERSION\" ] && [ -n \"$PS1\" ]; then\n");
script.push_str(" # Use DEBUG trap to capture commands before execution\n");
script.push_str(" trap 'alman_preexec \"$BASH_COMMAND\"' DEBUG\n");
script.push_str("fi\n\n");
script.push_str("# Source aliases on shell startup (don't track this command)\n");
script.push_str("alman_source_aliases\n");
script
}
fn render_zsh(opts: &ShellOpts) -> String {
let mut script = String::new();
script.push_str("# Alman shell integration for zsh\n");
script.push_str("# Add this to your ~/.zshrc\n\n");
script.push_str(&format!("export ALMAN_DATA_DIR=\"{}\"\n", opts.data_dir));
script.push_str(&format!("export ALMAN_CONFIG_DIR=\"{}\"\n", opts.config_dir));
script.push_str(&format!("export ALMAN_BIN=\"{}\"\n\n", opts.app_path));
script.push_str("alman_preexec() {\n");
script.push_str(" if [ -n \"$1\" ]; then\n");
script.push_str(&format!(" {} custom \"$1\" 2>/dev/null\n", opts.app_path));
script.push_str(" fi\n");
script.push_str("}\n\n");
script.push_str("alman_source_aliases() {\n");
script.push_str(" # Source all alias files from alman config\n");
script.push_str(&format!(" if [ -f \"{}/config.json\" ]; then\n", opts.config_dir));
script.push_str(" # Extract alias file paths from config and source them\n");
script.push_str(" local alias_files=$(cat \"$ALMAN_CONFIG_DIR/config.json\" | grep -o '\"[^\"]*aliases[^\"]*\"' | tr -d '\"')\n");
script.push_str(" for file in $alias_files; do\n");
script.push_str(" if [ -f \"$file\" ]; then\n");
script.push_str(" source \"$file\"\n");
script.push_str(" fi\n");
script.push_str(" done\n");
script.push_str(" else\n");
script.push_str(&format!(" # Fallback to default alias file\n"));
script.push_str(&format!(" if [ -f \"{}\" ]; then\n", opts.alias_file_path));
script.push_str(&format!(" source \"{}\"\n", opts.alias_file_path));
script.push_str(" fi\n");
script.push_str(" fi\n");
script.push_str("}\n\n");
script.push_str("autoload -U add-zsh-hook\n");
script.push_str("add-zsh-hook preexec alman_preexec\n\n");
script.push_str("# Source aliases on shell startup\n");
script.push_str("alman_source_aliases\n");
script
}
fn render_fish(opts: &ShellOpts) -> String {
let mut script = String::new();
script.push_str("# Alman shell integration for fish\n");
script.push_str("# Add this to your ~/.config/fish/config.fish\n\n");
script.push_str(&format!("set -gx ALMAN_DATA_DIR \"{}\"\n", opts.data_dir));
script.push_str(&format!("set -gx ALMAN_CONFIG_DIR \"{}\"\n", opts.config_dir));
script.push_str(&format!("set -gx ALMAN_BIN \"{}\"\n\n", opts.app_path));
script.push_str("function alman_preexec --on-event fish_preexec\n");
script.push_str(" if test -n \"$argv[1]\"\n");
script.push_str(&format!(" {} custom \"$argv[1]\" 2>/dev/null\n", opts.app_path));
script.push_str(" end\n");
script.push_str("end\n\n");
script.push_str("function alman_source_aliases\n");
script.push_str(" # Source all alias files from alman config\n");
script.push_str(&format!(" if test -f \"{}/config.json\"\n", opts.config_dir));
script.push_str(" # Extract alias file paths from config and source them\n");
script.push_str(" for file in (cat \"$ALMAN_CONFIG_DIR/config.json\" | grep -o '\"[^\"]*aliases[^\"]*\"' | tr -d '\"')\n");
script.push_str(" if test -f \"$file\"\n");
script.push_str(" source \"$file\"\n");
script.push_str(" end\n");
script.push_str(" end\n");
script.push_str(" else\n");
script.push_str(&format!(" # Fallback to default alias file\n"));
script.push_str(&format!(" if test -f \"{}\"\n", opts.alias_file_path));
script.push_str(&format!(" source \"{}\"\n", opts.alias_file_path));
script.push_str(" end\n");
script.push_str(" end\n");
script.push_str("end\n\n");
script.push_str("# Source aliases on shell startup\n");
script.push_str("alman_source_aliases\n");
script
}
fn render_posix(opts: &ShellOpts) -> String {
let mut script = String::new();
script.push_str("# Alman shell integration for POSIX shells (ksh, dash, etc.)\n");
script.push_str("# Add this to your ~/.profile or ~/.kshrc\n\n");
script.push_str(&format!("export ALMAN_DATA_DIR=\"{}\"\n", opts.data_dir));
script.push_str(&format!("export ALMAN_CONFIG_DIR=\"{}\"\n", opts.config_dir));
script.push_str(&format!("export ALMAN_BIN=\"{}\"\n\n", opts.app_path));
script.push_str("alman_preexec() {\n");
script.push_str(" if [ -n \"$1\" ]; then\n");
script.push_str(&format!(" {} custom \"$1\" 2>/dev/null\n", opts.app_path));
script.push_str(" fi\n");
script.push_str("}\n\n");
script.push_str("alman_source_aliases() {\n");
script.push_str(" # Source all alias files from alman config\n");
script.push_str(&format!(" if [ -f \"{}/config.json\" ]; then\n", opts.config_dir));
script.push_str(" # Extract alias file paths from config and source them\n");
script.push_str(" local alias_files=$(cat \"$ALMAN_CONFIG_DIR/config.json\" | grep -o '\"[^\"]*aliases[^\"]*\"' | tr -d '\"')\n");
script.push_str(" for file in $alias_files; do\n");
script.push_str(" if [ -f \"$file\" ]; then\n");
script.push_str(" . \"$file\"\n");
script.push_str(" fi\n");
script.push_str(" done\n");
script.push_str(" else\n");
script.push_str(&format!(" # Fallback to default alias file\n"));
script.push_str(&format!(" if [ -f \"{}\" ]; then\n", opts.alias_file_path));
script.push_str(&format!(" . \"{}\"\n", opts.alias_file_path));
script.push_str(" fi\n");
script.push_str(" fi\n");
script.push_str("}\n\n");
script.push_str("# Note: POSIX shells don't have built-in preexec hooks\n");
script.push_str("# You may need to manually call alman_preexec in your PS1\n");
script.push_str("# Example: PS1='$(alman_preexec $?) $ '\n\n");
script.push_str("# Source aliases on shell startup\n");
script.push_str("alman_source_aliases\n");
script
}