envmgr 0.1.1

Dotfiles and CLI environment manager
Documentation
use std::path::Path;

use clap::{CommandFactory, Parser};
use envmgr::cli::{Args, Command, Shell};
use envmgr::config::BASE_ENV_NAME;
use envmgr::environment::EnvironmentManager;
use envmgr::error::EnvMgrResult;
use indoc::indoc;
use log::info;

fn make_fish_hook(bin_name: &str) -> String {
    indoc! {r#"
    # envmgr fish hook

    # Re-apply env on prompt draw
    function __envmgr_export_eval --on-event fish_prompt
        command BIN_NAME use | source
    end"#}
    .replace("BIN_NAME", bin_name)
}

fn main() -> EnvMgrResult<()> {
    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
        .format_timestamp(None)
        .format_module_path(false)
        .format_source_path(false)
        .format_target(false)
        .init();
    let cli = Args::parse();

    let bin_name = std::env::args()
        .next()
        .and_then(|p| {
            Path::new(&p)
                .file_name()
                .map(|s| s.to_string_lossy().into_owned())
        })
        .filter(|s: &String| !s.is_empty())
        .unwrap_or_else(|| "envmgr".to_string());

    match &cli.command {
        Command::Init { force } => {
            info!("Initializing environment manager. Force: {}", force);
            todo!("Implement init functionality");
        }
        Command::Hook { shell } => match shell {
            Shell::Fish => {
                println!("{}", make_fish_hook(&bin_name));
                Ok(())
            }
        },
        Command::Add { name } => {
            info!("Adding a new environment. Name: {}", name);
            todo!("Implement add functionality");
        }
        Command::List => {
            info!("Listing all environments.");
            let environments = EnvironmentManager::list_environments()?;
            for (current, env) in environments {
                eprintln!(
                    "{} {} - {}",
                    if current { "*" } else { " " },
                    env.key,
                    env.name
                );
            }
            Ok(())
        }
        Command::Remove { name } => {
            info!("Removing environment: {}", name);
            todo!("Implement remove functionality");
        }
        Command::Use => {
            let em = EnvironmentManager { shell: Shell::Fish };
            em.use_environment()
        }
        Command::Link => EnvironmentManager::link_files(),
        Command::Switch { name } => {
            if name == BASE_ENV_NAME {
                return EnvironmentManager::switch_base_environment();
            }
            EnvironmentManager::switch_environment_by_key(name)
        }
        Command::Doctor => {
            info!("Running health check.");
            todo!("Implement doctor functionality");
        }
        Command::Completions { shell } => {
            let mut cmd = Args::command();
            clap_complete::generate(*shell, &mut cmd, &bin_name, &mut std::io::stdout());
            eprintln!(
                "Usage: {bin_name} completions fish > ~/.config/fish/completions/{bin_name}.fish"
            );
            Ok(())
        }
    }
}