slick 0.22.0

async ZSH prompt
Documentation
pub mod context;
pub mod git;
pub mod precmd;
pub mod prompt;

use std::env;
use std::sync::OnceLock;

// Cache for environment variable defaults to avoid repeated env::var() calls
static ENV_CACHE: OnceLock<EnvDefaults> = OnceLock::new();

struct EnvDefaults {
    aws_color: String,
    cmd_max_exec_time: String,
    cursor_shape: String,
    devpod_color: String,
    devpod_symbol: String,
    k8s_color: String,
    error_color: String,
    git_action_color: String,
    git_auth_color: String,
    git_auth_symbol: String,
    git_branch_color: String,
    git_branch_symbol: String,
    git_branch_symbol_color: String,
    git_fetch: String,
    git_main_branch_color: String,
    git_remote_color: String,
    git_remote_ahead: String,
    git_remote_behind: String,
    git_staged_color: String,
    git_status_color: String,
    git_uname_color: String,
    non_breaking_space: String,
    path_color: String,
    python_env_color: String,
    root_color: String,
    root_symbol: String,
    ssh_color: String,
    symbol: String,
    symbol_color: String,
    time_elapsed_color: String,
    transient: String,
    toolbox_color: String,
    toolbox_symbol: String,
    vicmd_color: String,
    vicmd_symbol: String,
    short_path: String,
    short_timestamp: String,
    auto_short_context: String,
}

impl EnvDefaults {
    fn new() -> Self {
        Self {
            aws_color: env::var("SLICK_PROMPT_AWS_COLOR").unwrap_or_else(|_| "7".into()),
            cmd_max_exec_time: env::var("SLICK_PROMPT_CMD_MAX_EXEC_TIME")
                .unwrap_or_else(|_| "5".into()),
            cursor_shape: env::var("SLICK_PROMPT_CURSOR_SHAPE").unwrap_or_else(|_| "4".into()),
            devpod_color: env::var("SLICK_PROMPT_DEVPOD_COLOR").unwrap_or_else(|_| "7".into()),
            devpod_symbol: env::var("SLICK_PROMPT_DEVPOD_SYMBOL").unwrap_or_else(|_| "".into()),
            k8s_color: env::var("SLICK_PROMPT_K8S_COLOR").unwrap_or_else(|_| "7".into()),
            error_color: env::var("SLICK_PROMPT_ERROR_COLOR").unwrap_or_else(|_| "196".into()),
            git_action_color: env::var("SLICK_PROMPT_GIT_ACTION_COLOR")
                .unwrap_or_else(|_| "3".into()),
            git_auth_color: env::var("SLICK_PROMPT_GIT_AUTH_COLOR")
                .unwrap_or_else(|_| "red".into()),
            git_auth_symbol: env::var("SLICK_PROMPT_GIT_AUTH_SYMBOL")
                .unwrap_or_else(|_| "🔒".into()),
            git_branch_color: env::var("SLICK_PROMPT_GIT_BRANCH_COLOR")
                .unwrap_or_else(|_| "3".into()),
            git_branch_symbol: env::var("SLICK_PROMPT_GIT_BRANCH_SYMBOL")
                .unwrap_or_else(|_| "".into()),
            git_branch_symbol_color: env::var("SLICK_PROMPT_GIT_BRANCH_SYMBOL_COLOR")
                .unwrap_or_else(|_| "2".into()),
            git_fetch: env::var("SLICK_PROMPT_GIT_FETCH").unwrap_or_else(|_| "1".into()),
            git_main_branch_color: env::var("SLICK_PROMPT_GIT_MAIN_BRANCH_COLOR")
                .or_else(|_| env::var("SLICK_PROMPT_GIT_MASTER_BRANCH_COLOR"))
                .unwrap_or_else(|_| "160".into()),
            git_remote_color: env::var("SLICK_PROMPT_GIT_REMOTE_COLOR")
                .unwrap_or_else(|_| "6".into()),
            git_remote_ahead: env::var("SLICK_PROMPT_GIT_REMOTE_AHEAD")
                .unwrap_or_else(|_| "\u{21e1}".into()),
            git_remote_behind: env::var("SLICK_PROMPT_GIT_REMOTE_BEHIND")
                .unwrap_or_else(|_| "\u{21e3}".into()),
            git_staged_color: env::var("SLICK_PROMPT_GIT_STAGED_COLOR")
                .unwrap_or_else(|_| "7".into()),
            git_status_color: env::var("SLICK_PROMPT_GIT_STATUS_COLOR")
                .unwrap_or_else(|_| "5".into()),
            git_uname_color: env::var("SLICK_PROMPT_GIT_UNAME_COLOR")
                .unwrap_or_else(|_| "8".into()),
            non_breaking_space: env::var("SLICK_PROMPT_NON_BREAKING_SPACE")
                .unwrap_or_else(|_| "\u{a0}".into()),
            path_color: env::var("SLICK_PROMPT_PATH_COLOR").unwrap_or_else(|_| "74".into()),
            python_env_color: env::var("SLICK_PROMPT_PYTHON_ENV_COLOR")
                .unwrap_or_else(|_| "7".into()),
            root_color: env::var("SLICK_PROMPT_ROOT_COLOR").unwrap_or_else(|_| "1".into()),
            root_symbol: env::var("SLICK_PROMPT_ROOT_SYMBOL").unwrap_or_else(|_| "#".into()),
            ssh_color: env::var("SLICK_PROMPT_SSH_COLOR").unwrap_or_else(|_| "8".into()),
            symbol: env::var("SLICK_PROMPT_SYMBOL").unwrap_or_else(|_| "$".into()),
            symbol_color: env::var("SLICK_PROMPT_SYMBOL_COLOR").unwrap_or_else(|_| "5".into()),
            time_elapsed_color: env::var("SLICK_PROMPT_TIME_ELAPSED_COLOR")
                .unwrap_or_else(|_| "3".into()),
            transient: env::var("SLICK_PROMPT_TRANSIENT").unwrap_or_else(|_| "1".into()),
            toolbox_color: env::var("SLICK_PROMPT_TOOLBOX_COLOR").unwrap_or_else(|_| "3".into()),
            toolbox_symbol: env::var("SLICK_PROMPT_TOOLBOX_SYMBOL").unwrap_or_else(|_| "".into()),
            vicmd_color: env::var("SLICK_PROMPT_VICMD_COLOR").unwrap_or_else(|_| "3".into()),
            vicmd_symbol: env::var("SLICK_PROMPT_VICMD_SYMBOL").unwrap_or_else(|_| ">".into()),
            short_path: env::var("SLICK_PROMPT_SHORT_PATH").unwrap_or_else(|_| "0".into()),
            short_timestamp: env::var("SLICK_PROMPT_SHORT_TIMESTAMP")
                .unwrap_or_else(|_| "0".into()),
            auto_short_context: env::var("SLICK_PROMPT_AUTO_SHORT_CONTEXT")
                .unwrap_or_else(|_| "0".into()),
        }
    }
}

#[must_use]
pub fn get_env(e: &str) -> &str {
    let cache = ENV_CACHE.get_or_init(EnvDefaults::new);

    match e {
        "SLICK_PROMPT_AWS_COLOR" => &cache.aws_color,
        "SLICK_PROMPT_CMD_MAX_EXEC_TIME" => &cache.cmd_max_exec_time,
        "SLICK_PROMPT_CURSOR_SHAPE" => &cache.cursor_shape,
        "SLICK_PROMPT_DEVPOD_COLOR" => &cache.devpod_color,
        "SLICK_PROMPT_DEVPOD_SYMBOL" => &cache.devpod_symbol,
        "SLICK_PROMPT_ERROR_COLOR" => &cache.error_color,
        "SLICK_PROMPT_GIT_ACTION_COLOR" => &cache.git_action_color,
        "SLICK_PROMPT_GIT_AUTH_COLOR" => &cache.git_auth_color,
        "SLICK_PROMPT_GIT_AUTH_SYMBOL" => &cache.git_auth_symbol,
        "SLICK_PROMPT_GIT_BRANCH_COLOR" => &cache.git_branch_color,
        "SLICK_PROMPT_GIT_BRANCH_SYMBOL" => &cache.git_branch_symbol,
        "SLICK_PROMPT_GIT_BRANCH_SYMBOL_COLOR" => &cache.git_branch_symbol_color,
        "SLICK_PROMPT_GIT_FETCH" => &cache.git_fetch,
        "SLICK_PROMPT_GIT_MAIN_BRANCH_COLOR" | "SLICK_PROMPT_GIT_MASTER_BRANCH_COLOR" => {
            &cache.git_main_branch_color
        }
        "SLICK_PROMPT_GIT_REMOTE_COLOR" => &cache.git_remote_color,
        "SLICK_PROMPT_GIT_REMOTE_AHEAD" => &cache.git_remote_ahead,
        "SLICK_PROMPT_GIT_REMOTE_BEHIND" => &cache.git_remote_behind,
        "SLICK_PROMPT_GIT_STAGED_COLOR" => &cache.git_staged_color,
        "SLICK_PROMPT_GIT_STATUS_COLOR" => &cache.git_status_color,
        "SLICK_PROMPT_GIT_UNAME_COLOR" => &cache.git_uname_color,
        "SLICK_PROMPT_NON_BREAKING_SPACE" => &cache.non_breaking_space,
        "SLICK_PROMPT_K8S_COLOR" => &cache.k8s_color,
        "SLICK_PROMPT_PATH_COLOR" => &cache.path_color,
        "SLICK_PROMPT_PYTHON_ENV_COLOR" => &cache.python_env_color,
        "SLICK_PROMPT_ROOT_COLOR" => &cache.root_color,
        "SLICK_PROMPT_ROOT_SYMBOL" => &cache.root_symbol,
        "SLICK_PROMPT_SSH_COLOR" => &cache.ssh_color,
        "SLICK_PROMPT_SYMBOL" => &cache.symbol,
        "SLICK_PROMPT_SYMBOL_COLOR" => &cache.symbol_color,
        "SLICK_PROMPT_TIME_ELAPSED_COLOR" => &cache.time_elapsed_color,
        "SLICK_PROMPT_TRANSIENT" => &cache.transient,
        "SLICK_PROMPT_TOOLBOX_COLOR" => &cache.toolbox_color,
        "SLICK_PROMPT_TOOLBOX_SYMBOL" => &cache.toolbox_symbol,
        "SLICK_PROMPT_VICMD_COLOR" => &cache.vicmd_color,
        "SLICK_PROMPT_VICMD_SYMBOL" => &cache.vicmd_symbol,
        "SLICK_PROMPT_SHORT_PATH" => &cache.short_path,
        "SLICK_PROMPT_SHORT_TIMESTAMP" => &cache.short_timestamp,
        "SLICK_PROMPT_AUTO_SHORT_CONTEXT" => &cache.auto_short_context,
        _ => "??",
    }
}

// For environment variables that aren't cached in EnvDefaults, use this function
#[must_use]
pub fn get_env_var(e: &str) -> String {
    env::var(e).unwrap_or_default()
}

#[must_use]
pub fn get_env_var_or(e: &str, default: &str) -> String {
    env::var(e).unwrap_or_else(|_| default.to_owned())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_env_returns_default_symbol() {
        // Test with an env var that's unlikely to be set in test environment
        assert_eq!(get_env("SLICK_PROMPT_SYMBOL"), "$");
    }

    #[test]
    fn test_get_env_returns_default_colors() {
        assert_eq!(get_env("SLICK_PROMPT_ERROR_COLOR"), "196");
        assert_eq!(get_env("SLICK_PROMPT_PATH_COLOR"), "74");
        assert_eq!(get_env("SLICK_PROMPT_GIT_BRANCH_COLOR"), "3");
        assert_eq!(get_env("SLICK_PROMPT_GIT_BRANCH_SYMBOL_COLOR"), "2");
        assert_eq!(get_env("SLICK_PROMPT_GIT_MAIN_BRANCH_COLOR"), "160");
        assert_eq!(get_env("SLICK_PROMPT_GIT_MASTER_BRANCH_COLOR"), "160");
    }

    #[test]
    fn test_get_env_unknown_returns_question_marks() {
        assert_eq!(get_env("UNKNOWN_VAR"), "??");
    }

    #[test]
    fn test_get_env_var_returns_empty_for_missing() {
        // Test with an env var that's unlikely to be set
        assert_eq!(get_env_var("UNLIKELY_TO_EXIST_VAR_12345"), "");
    }

    #[test]
    fn test_get_env_var_or_returns_default() {
        assert_eq!(
            get_env_var_or("UNLIKELY_TO_EXIST_VAR_12345", "default"),
            "default"
        );
    }
}