huber 1.0.11

Huber, simplify GitHub package management
Documentation
use std::fs::{read_dir, remove_dir_all, remove_file};
use std::sync::Arc;

use anyhow::anyhow;
use async_trait::async_trait;
use log::debug;
use semver::Version;
use simpledi_rs::di::{DIContainer, DIContainerExtTrait, DependencyInjectTrait};

use crate::cmd::PlatformStdLib;
use crate::model::config::{Config, ConfigPath};
use crate::service::package::PackageService;
use crate::service::release::ReleaseService;
use crate::service::{ItemOperationTrait, ServiceTrait};

pub trait UpdateTrait {
    fn reset(&self) -> anyhow::Result<()>;
}

#[async_trait]
pub trait UpdateAsyncTrait {
    async fn has_update(&self) -> anyhow::Result<(bool, String)>;
    async fn update(&self, prefer_stdlib: &PlatformStdLib) -> anyhow::Result<()>;
}

#[derive(Debug)]
pub struct HuberUpdateService {
    pub container: Option<Arc<DIContainer>>,
}

unsafe impl Send for HuberUpdateService {}

unsafe impl Sync for HuberUpdateService {}

impl ServiceTrait for HuberUpdateService {}

impl DependencyInjectTrait for HuberUpdateService {
    fn inject(&mut self, container: Arc<DIContainer>) {
        self.container = Some(container);
    }
}

impl Default for HuberUpdateService {
    fn default() -> Self {
        Self::new()
    }
}

impl HuberUpdateService {
    pub fn new() -> Self {
        Self { container: None }
    }
}

impl UpdateTrait for HuberUpdateService {
    fn reset(&self) -> anyhow::Result<()> {
        let config = self.container.get::<Config>().unwrap();

        let bin_dir_path = config.bin_dir()?;
        if bin_dir_path.exists() {
            for entry in read_dir(bin_dir_path)? {
                let path = entry?.path();

                if path.file_name().unwrap().to_str().unwrap() == "huber" {
                    debug!("Keeping huber executable");

                    let option = fs_extra::file::CopyOptions::new();
                    let temp_path = path.parent().unwrap().join("huber_temp");

                    debug!("Coping {:?} to {:?}", &path, &temp_path);
                    let _ = remove_file(&temp_path);
                    fs_extra::file::copy(&path, &temp_path, &option)?;

                    debug!("Moving {:?} to {:?}", &temp_path, &path);
                    let _ = remove_file(&path);
                    fs_extra::file::move_file(&temp_path, &path, &option)?;

                    continue;
                }

                let _ = remove_dir_all(path);
            }
        }

        let _ = remove_dir_all(config.installed_pkg_root_dir()?);
        let _ = remove_dir_all(config.temp_dir()?);
        let _ = remove_dir_all(config.repo_root_dir()?);
        let _ = remove_file(config.lock_file()?);
        let _ = remove_file(config.config_file()?);

        Ok(())
    }
}

#[async_trait]
impl UpdateAsyncTrait for HuberUpdateService {
    async fn has_update(&self) -> anyhow::Result<(bool, String)> {
        let pkg_service = self.container.get::<PackageService>().unwrap();
        let release_service = self.container.get::<ReleaseService>().unwrap();

        let current_version =
            Version::parse(env!("HUBER_VERSION").trim_start_matches("v")).unwrap();
        let pkg = pkg_service.get("huber")?;

        match release_service.get_latest(&pkg).await {
            Err(e) => Err(anyhow!("No update available: {:?}", e)),
            Ok(r) => {
                let result = Version::parse(r.version.trim_start_matches("v"))
                    .map(|ver| ver > current_version);

                match result {
                    Ok(update_needed) => Ok((update_needed, r.version)),
                    Err(e) => Err(anyhow!(
                        "A update available, but failed to continue: {:?}",
                        e
                    )),
                }
            }
        }
    }

    async fn update(&self, prefer_stdlib: &PlatformStdLib) -> anyhow::Result<()> {
        let pkg_service = self.container.get::<PackageService>().unwrap();
        let release_service = self.container.get::<ReleaseService>().unwrap();

        let mut pkg = pkg_service.get("huber")?;
        let release = release_service.get_latest(&pkg).await?;
        pkg.version = Some(release.version);

        release_service.update(&pkg, prefer_stdlib, true).await?;
        Ok(())
    }
}