crosup 0.5.2

A simple CLI tool to quickly setup your development environment on Chomebook (ChromeOS) or any Linux Distribution.
use crate::cmd::{init::execute_init, install::execute_install};
use anyhow::Error;
use clap::{arg, Command};
use cmd::{add::execute_add, diff::execute_diff, history::execute_history, search::execute_search};
use crosup_types::configuration::ConfigFormat;
use types::{InstallArgs, SearchArgs};

pub mod cmd;
pub mod macros;
pub mod types;

fn cli() -> Command<'static> {
    const VERSION: &'static str = env!("CARGO_PKG_VERSION");
    Command::new("crosup")
        .version(VERSION)
        .about(
            r#"
             ______                __  __    
            / ____/________  _____/ / / /___ 
           / /   / ___/ __ \/ ___/ / / / __ \
          / /___/ /  / /_/ (__  ) /_/ / /_/ /
          \____/_/   \____/____/\____/ .___/ 
                                    /_/      

Quickly install your development tools on your new Chromebook, MacOS or any Linux distribution"#,
        )
        .author("Tsiry Sandratraina <tsiry.sndr@fluentci.io>")
        .subcommand(
            Command::new("init")
            .arg(arg!(--toml "Generate a default configuration file in toml format"))
            .arg(arg!(--inventory -i "Generate a default inventory file"))
            .arg(arg!([packages]... "List of packages to install"))
            .about("Generate a default configuration file"),
        )
        .subcommand(
            Command::new("install")
                .arg(arg!(--ask -a "Ask for confirmation before installing tools"))
                .arg(arg!([tools]... "List of tools to install, e.g. docker, nix, devbox, homebrew, fish, vscode, ble.sh ..."))
                .arg(arg!(--remote -r [ip] "Install tools on a remote machine"))
                .arg(arg!(--port -p [port] "Port to use when connecting to the remote machine"))
                .arg(
                    arg!(--username -u [username] "Username to use when connecting to the remote machine"),
                )
                .arg(arg!(--invetory -i [inventory] "Path to the inventory file (list of remote machines) in HCL or TOML format"))
                .about(
                    "Install developer tools, e.g. docker, nix, devbox, homebrew, fish, vscode, ble.sh ...",
                ),
        )
        .subcommand(
            Command::new("diff")
                .about("Show the difference between the current configuration and the previous one"),
        )
        .subcommand(
            Command::new("history")
                .about("Show the change history of the configuration file"),
        )
        .subcommand(
            Command::new("add")
                .arg(arg!(--ask -a "Ask for confirmation before adding a new tool"))
                .arg(arg!(<tools>... "Tools to add to the configuration file, e.g. gh, vim, tig ..."))
                .about("Add a new tool to the configuration file"),
        )
        .subcommand(
            Command::new("search")
                .arg(arg!(--channel -c [channel] "Channel to use when searching for a package"))
                .arg(arg!(--max-results -m [max_results] "Maximum number of results to return"))
                .arg(arg!(<package> "Package to search for"))
                .about("Search for a package in the nixpkgs repository"),
        )
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    let matches = cli().get_matches();
    match matches.subcommand() {
        Some(("install", args)) => {
            let ask = args.is_present("ask");
            let tools = args.values_of("tools").map(|tools| {
                tools
                    .into_iter()
                    .map(|tool| tool.to_string())
                    .collect::<Vec<String>>()
            });
            let remote_is_present = args.is_present("remote");
            let remote = args.value_of("remote").map(|remote| remote.to_string());
            let port = args
                .value_of("port")
                .map(|port| port.parse::<u16>().unwrap());
            let username = args
                .value_of("username")
                .map(|username| username.to_string());
            let inventory = args
                .value_of("inventory")
                .map(|inventory| inventory.to_string());

            execute_install(InstallArgs {
                ask,
                tools,
                remote_is_present,
                remote,
                username,
                inventory,
                port,
            })
            .await?;
        }
        Some(("init", args)) => {
            let toml = args.is_present("toml");
            let inventory = args.is_present("inventory");
            let packages = args.values_of("packages").map(|packages| {
                packages
                    .into_iter()
                    .map(|package| package.to_string())
                    .collect::<Vec<String>>()
            });
            match toml {
                true => execute_init(ConfigFormat::TOML, inventory, packages)?,
                false => execute_init(ConfigFormat::HCL, inventory, packages)?,
            }
        }
        Some(("diff", _)) => {
            execute_diff().await?;
        }
        Some(("history", _)) => {
            execute_history().await?;
        }
        Some(("add", args)) => {
            let tools = args
                .values_of("tools")
                .map(|tool| {
                    tool.into_iter()
                        .map(|tool| tool.to_string())
                        .collect::<Vec<String>>()
                })
                .unwrap();
            let ask = args.is_present("ask");
            execute_add(tools, ask).await?;
        }
        Some(("search", args)) => {
            let package = args.value_of("package").unwrap();
            let channel = args.value_of("channel").unwrap_or("unstable");
            let max_results = args
                .value_of("max_results")
                .map(|max_results| max_results.parse::<u32>().unwrap())
                .unwrap_or(10);
            let args = SearchArgs {
                package: package.to_string(),
                channel: channel.to_string(),
                max_results,
            };
            execute_search(args).await?;
        }
        _ => {
            cli().print_help().unwrap();
        }
    }

    Ok(())
}