asdfg 0.2.0

Installing global asdf packages from a YAML config
Documentation
use crate::config;
use colored::*;
use std::error::Error;
use std::process;

const BASE_CMD: &str = "asdf";
const VERSION_LATEST: &str = "latest";

pub fn add_plugin(name: &str) -> Result<(), Box<dyn Error>> {
    let desc = format!("Adding asdf plugin");
    let error = format!("Plugin {} could not be installed.", name).red();
    run_cmd(&["plugin add", name], &desc, &error)?;
    eprintln!("---");

    Ok(())
}

pub fn install_package(package: &config::Package) -> Result<(), Box<dyn Error>> {
    for version in &package.versions {
        let name = &package.name;
        install_version(name, version)?;
        set_global(name, version)?;
        reshim(name)?;
    }

    Ok(())
}

fn install_version(name: &String, version: &String) -> Result<(), Box<dyn Error>> {
    let desc = format!("Installing {} ({})", name, version);
    let error = format!("{} v{} could not be installed", name, version).red();
    run_cmd(&["install", &name, &version], &desc, &error)?;

    Ok(())
}

fn set_global(name: &String, version: &String) -> Result<(), Box<dyn Error>> {
    let mut final_version = &version.clone();
    let available_versions = run_cmd_silent(&["list", name])?;
    let last_version = available_versions.last().unwrap();

    if final_version == VERSION_LATEST {
        final_version = last_version;
    }

    let desc = format!("Setting global {} version to {}", name, final_version);
    let error = format!("Failed to use global {} v{}", name, final_version);
    run_cmd(&["global", &name, &final_version], &desc, &error)?;

    Ok(())
}

fn reshim(name: &String) -> Result<(), Box<dyn Error>> {
    let error = format!("Failed to reshim {}", name);
    run_cmd(&["reshim", &name], "", &error)?;

    Ok(())
}

fn run_cmd(args: &[&str], desc: &str, error: &str) -> Result<(), Box<dyn Error>> {
    eprintln!("{}", desc);

    let output = process::Command::new(BASE_CMD)
        .args(args)
        .output()
        .expect(error);

    if !output.stdout.is_empty() {
        eprintln!("{}", String::from_utf8(output.stdout).unwrap().green());
    }

    if !output.stderr.is_empty() {
        eprintln!("{}", String::from_utf8(output.stderr).unwrap().yellow());
    }

    Ok(())
}

fn run_cmd_silent(args: &[&str]) -> Result<Vec<String>, Box<dyn Error>> {
    let output = process::Command::new(BASE_CMD).args(args).output().unwrap();

    if !output.stdout.is_empty() {
        return Ok(String::from_utf8(output.stdout)
            .unwrap()
            .split("\n")
            .map(|s| s.trim().to_string())
            .filter(|s| !s.is_empty() || s.starts_with("--"))
            .collect::<Vec<_>>());
    }

    if !output.stderr.is_empty() {
        return Ok(vec![String::from_utf8(output.stderr).unwrap()]);
    }

    Ok(vec![])
}