vers 0.5.0

Install and manage dev tools with multiple environments
Documentation
mod add;
mod completions;
mod list;
mod remove;
mod sync;
mod update;

pub use add::{add_new_tool, Patterns};
pub use completions::generate_completions;
pub use list::list_tools;
pub use remove::remove_tool;
pub use sync::sync_tools;
pub use update::{update_tools, UpdateType};

use crate::{
    environment::{Environment, EnvironmentError},
    github::{self, GitHubError},
    system::{OperatingSystem, PlatformArchitecture, System},
    tool::Tool,
    version::Version,
};
use thiserror::Error;

#[derive(Debug, Error)]
pub enum CliActionError {
    #[error("Failed to delete file '{file_name}'(symlink? {symlink}). {source}")]
    FileDelete {
        file_name: std::path::PathBuf,
        symlink: bool,
        source: std::io::Error,
    },
    #[error("Failed to delete directory '{directory}. {source}")]
    DirectoryDelete {
        directory: std::path::PathBuf,
        source: std::io::Error,
    },
    #[error("Tool '{tool_name}' not found in the '{env_name}' environment")]
    ToolNotFound { tool_name: String, env_name: String },
    #[error("The environment {0}, does not contain any tools")]
    EmptyEnvironment(String),
    #[error("Unable to find release for {0}")]
    ReleaseNotFound(String),
    #[error("Unable to find asset for {tool_name}@{version} for OS: {os}; Arch: {arch}")]
    AssetNotFound {
        tool_name: String,
        version: Version,
        arch: PlatformArchitecture,
        os: OperatingSystem,
    },
    #[error("...")]
    GitHub(#[from] GitHubError),
    #[error("...")]
    Environment(#[from] EnvironmentError),
}

type Result<T, E = CliActionError> = std::result::Result<T, E>;

async fn handle_tool_install(
    env: &mut Environment,
    tool: &'_ Tool,
    system: &'_ System,
    version: Option<Version>,
) -> Result<()> {
    let split_org_repo: Vec<&str> = tool.name.split('/').collect();
    let owner = split_org_repo[0];
    let repo = split_org_repo[1];

    let version = match version {
        Some(v) => v,
        None => match github::get_latest_release_tag(owner, repo).await {
            Some(rel) => rel,
            None => return Err(CliActionError::ReleaseNotFound(tool.name.to_string())),
        },
    };

    if tool.current_version != version.as_tag() {
        let release = github::get_specific_release_for_repo(owner, repo, &version).await?;

        let asset = github::get_platform_specific_asset(&release, system, &tool.asset_pattern);
        if asset.is_none() {
            return Err(CliActionError::AssetNotFound {
                tool_name: tool.name.to_string(),
                version,
                arch: system.architecture.clone(),
                os: system.os.clone(),
            });
        }
        let asset = asset.unwrap();
        match env
            .add_tool(
                &tool.name,
                &tool.alias,
                version,
                asset,
                &tool.asset_pattern,
                &tool.file_pattern,
            )
            .await
        {
            Ok(_) => (),
            Err(add_tool_err) => return Err(add_tool_err.into()),
        };
    };
    Ok(())
}