upstream-rs 1.4.2

Fetch package updates directly from the source.
use anyhow::Result;
use console::style;
use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
use std::time::{Duration, Instant};

use crate::{
    application::operations::install_operation::InstallOperation,
    models::{
        common::enums::{Channel, Filetype, Provider},
        upstream::Package,
    },
    providers::provider_manager::ProviderManager,
    services::storage::{config_storage::ConfigStorage, package_storage::PackageStorage},
    utils::static_paths::UpstreamPaths,
};

pub async fn run(
    name: String,
    repo_slug: String,
    kind: Filetype,
    version: Option<String>,
    provider: Provider,
    base_url: Option<String>,
    channel: Channel,
    match_pattern: Option<String>,
    exclude_pattern: Option<String>,
    create_entry: bool,
    ignore_checksums: bool,
) -> Result<()> {
    const PROGRESS_UPDATE_INTERVAL: Duration = Duration::from_millis(100);

    println!(
        "{}",
        style(format!("Installing {} from {} ...", &name, &provider)).cyan()
    );

    let paths = UpstreamPaths::new();

    let config = ConfigStorage::new(&paths.config.config_file)?;
    let mut package_storage = PackageStorage::new(&paths.config.packages_file)?;

    let github_token = config.get_config().github.api_token.as_deref();
    let gitlab_token = config.get_config().gitlab.api_token.as_deref();
    let gitea_token = config.get_config().gitea.api_token.as_deref();

    let provider_manager =
        ProviderManager::new(github_token, gitlab_token, gitea_token, base_url.as_deref())?;

    let mut package_installer =
        InstallOperation::new(&provider_manager, &mut package_storage, &paths)?;

    let package = Package::with_defaults(
        name,
        repo_slug,
        kind,
        match_pattern,
        exclude_pattern,
        channel,
        provider,
        base_url,
    );

    let pb = ProgressBar::new(0);
    pb.set_draw_target(ProgressDrawTarget::stderr_with_hz(10));
    pb.set_style(ProgressStyle::with_template(
        "{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta}) {msg}",
    )?);
    pb.enable_steady_tick(Duration::from_millis(120));

    // Borrow pb for the closures
    let pb_ref = &pb;
    let mut last_emit: Option<Instant> = None;
    let mut last_progress: Option<(u64, u64)> = None;
    let mut download_progress_callback = Some(|downloaded: u64, total: u64| {
        last_progress = Some((downloaded, total));
        let should_emit = last_emit
            .map(|t| t.elapsed() >= PROGRESS_UPDATE_INTERVAL)
            .unwrap_or(true);
        if should_emit {
            pb_ref.set_length(total);
            pb_ref.set_position(downloaded);
            last_emit = Some(Instant::now());
        }
    });

    let mut message_callback = Some(move |msg: &str| {
        pb_ref.println(msg);
    });

    package_installer
        .install_single(
            package,
            &version,
            &create_entry,
            ignore_checksums,
            &mut download_progress_callback,
            &mut message_callback,
        )
        .await?;

    if let Some((downloaded, total)) = last_progress {
        pb.set_length(total);
        pb.set_position(downloaded);
    }

    // Set pb to 100%
    pb.set_position(pb.length().unwrap_or(0));

    pb.finish_with_message("Install complete");
    println!("{}", style("Install complete.").green());

    Ok(())
}