huber 0.3.3

Huber package, simplify `GitHub release` package management with a live awesome list of GitHub project
use std::io::stdout;

use async_trait::async_trait;
use clap::{App, Arg, ArgMatches};
use libcli_rs::output::{OutputFactory, OutputTrait};
use simpledi_rs::di::{DIContainer, DIContainerTrait};

use huber_common::model::config::Config;
use huber_common::model::release::SortModelTrait;
use huber_common::result::Result;

use crate::cmd::{CommandAsyncTrait, CommandTrait};
use crate::service::package::PackageService;
use crate::service::release::{ReleaseService, ReleaseTrait};
use crate::service::{ItemOperationAsyncTrait, ItemOperationTrait};

pub(crate) const CMD_NAME: &str = "show";

#[derive(Debug)]
pub(crate) struct ShowCmd;

unsafe impl Send for ShowCmd {}

unsafe impl Sync for ShowCmd {}

impl ShowCmd {
    pub(crate) fn new() -> Self {
        Self {}
    }
}

impl<'a, 'b> CommandTrait<'a, 'b> for ShowCmd {
    fn app(&self) -> App<'a, 'b> {
        App::new(CMD_NAME)
            .visible_alias("s")
            .about("Shows installed packages")
            .args(&vec![
                Arg::with_name("name")
                    .value_name("package name")
                    .help("Package name")
                    .takes_value(true),
                Arg::with_name("all")
                    .short("a")
                    .long("all")
                    .help("Show all the installed versions"),
                Arg::with_name("detail")
                    .short("d")
                    .long("detail")
                    .help("Show the detailed info of release"),
            ])
    }
}

#[async_trait]
impl<'a, 'b> CommandAsyncTrait<'a, 'b> for ShowCmd {
    async fn run(
        &self,
        config: &Config,
        container: &DIContainer,
        matches: &ArgMatches<'a>,
    ) -> Result<()> {
        let pkg_service = container.get::<PackageService>().unwrap();
        let release_service = container.get::<ReleaseService>().unwrap();

        let mut excluded_keys = if matches.is_present("detail") {
            vec![]
        } else {
            vec!["package"]
        };

        if matches.is_present("name") {
            let name = matches.value_of("name").unwrap();

            if !release_service.has(name)? {
                return Err(anyhow!("{} not found", name));
            }

            let pkg = pkg_service.get(name)?;
            let release = release_service.current(&pkg)?;

            if matches.is_present("all") {
                let mut releases = release_service.find(&pkg).await?;
                releases.sort_by_version();

                releases = releases
                    .into_iter()
                    .map(|it| {
                        if it.current {
                            release_service.current(&it.package).unwrap()
                        } else {
                            it
                        }
                    })
                    .collect();

                return output!(config.output_format, .display(
                    stdout(),
                    &releases,
                    None,
                    Some(excluded_keys),
                ));
            }

            return output!(config.output_format, .display(
                stdout(),
                &release,
                None,
                Some(excluded_keys),
            ));
        }

        let mut current_releases = release_service.list()?;
        current_releases.sort_by_name();
        excluded_keys.push("executables");

        let releases = if matches.is_present("all") {
            let mut all_releases = vec![];

            for cr in current_releases.iter() {
                let mut releases = release_service.find(&cr.package).await?;
                releases.sort_by_version();
                all_releases.append(&mut releases);
            }

            all_releases
        } else {
            current_releases
        };

        output!(config.output_format, .display(
            stdout(),
            &releases,
            None,
            Some(excluded_keys),
        ))
    }
}