emplace 0.2.11-alpha.0

Command-line tool to mirror installed software on multiple machines.
#[macro_use]
extern crate clap;
#[macro_use]
extern crate strum_macros;

mod catch;
mod config;
mod git;
mod init;
mod install;
mod package;
mod repo;

use anyhow::Result;
use clap::{App, AppSettings, Arg, SubCommand};
use colored::*;
use log::{error, info};
use simplelog::{LevelFilter, TermLogger, TerminalMode};

use config::Config;
use repo::Repo;

fn public_clap_app<'a, 'b>() -> App<'a, 'b> {
    App::new("emplace")
        .version(crate_version!())
        .author(crate_authors!())
        .after_help("https://github.com/tversteeg/emplace")
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .subcommand(
            SubCommand::with_name("install")
                .about("Install the packages that have been mirrored from other machines"),
        )
        .subcommand(SubCommand::with_name("clean").about("Remove package synching"))
}

fn main() -> Result<()> {
    TermLogger::init(
        LevelFilter::Info,
        simplelog::Config::default(),
        TerminalMode::Mixed,
    )
    .expect("No interactive terminal");

    let matches = public_clap_app()
        .subcommand(
            SubCommand::with_name("init")
            .about("Prints the shell function used to execute emplace")
            .arg(
                Arg::with_name("shell")
                .value_name("SHELL")
                .help(
                    "The name of the currently running shell\nCurrently supported options: bash & zsh",
                )
                .required(true)
            )
        )
        .subcommand(
            SubCommand::with_name("catch")
                .about("Capture a command entired in a terminal")
                .arg(
                    Arg::with_name("line")
                        .value_name("LINE")
                        .help("The command as entired in the terminal")
                        .required(true),
                ),
        )
        .get_matches();

    match matches.subcommand() {
        ("init", Some(sub_m)) => {
            let shell_name = sub_m.value_of("shell").expect("Shell name is missing.");
            init::init_main(shell_name).expect("Could not initialize terminal script");
        }
        ("catch", Some(sub_m)) => {
            let line = sub_m.value_of("line").expect("Line is missing");
            let mut catches = catch::catch(line).expect("Could not parse line");

            if catches.0.is_empty() {
                // Nothing found, just return
                return Ok(());
            }

            // Filter out the packages that are already in the repository
            // Get the config
            let config = match Config::from_default_file().expect("Retrieving config went wrong") {
                Some(config) => config,
                None => Config::new().expect("Initializing new config failed"),
            };
            let repo = Repo::new(config).expect("Could not initialize git repository");
            catches.filter_saved_packages(
                &repo
                    .read()
                    .expect("Could not read packages file from repository"),
            );

            let len = catches.0.len();
            if len == 0 {
                // Nothing found after filtering
                return Ok(());
            }

            // Print the info
            match len {
                1 => info!("{}", "Mirror this command?".green().bold()),
                n => info!("{}", format!("Mirror these {} commands?", n).green().bold()),
            }
            for catch in catches.0.iter() {
                info!("- {}", catch.colour_full_name());
            }

            // Ask if it needs to be mirrored
            if !dialoguer::Confirmation::new()
                .interact()
                .expect("Could not create dialogue")
            {
                // Exit, we don't need to do anything
                return Ok(());
            }

            repo.mirror(catches).expect("Could not mirror commands");
        }
        ("install", Some(_)) => {
            // Get the config
            let config = match Config::from_default_file().expect("Retrieving config went wrong") {
                Some(config) => config,
                None => Config::new().expect("Initializing new config failed"),
            };

            let repo = Repo::new(config).expect("Could not initialize git repository");

            match repo.read() {
                Ok(packages) => {
                    if let Err(err) = crate::install::install(packages) {
                        error!("Could not install new changes: {}.", err);
                    }
                }
                Err(err) => error!("{}", err),
            };
        }
        ("clean", Some(_)) => {
            // Get the config
            let config = match Config::from_default_file().expect("Retrieving config went wrong") {
                Some(config) => config,
                None => Config::new().expect("Initializing new config failed"),
            };

            let repo = Repo::new(config).expect("Could not initialize git repository");

            match repo.read() {
                Ok(packages) => match crate::install::clean(packages) {
                    Ok(packages) => repo.clean(packages).expect("Could not clean repo."),
                    Err(err) => error!("Could not remove from repository: {}.", err),
                },
                Err(err) => error!("{}", err),
            };
        }
        (&_, _) => {}
    };

    Ok(())
}