stout 0.2.0

A fast, Rust-based Homebrew-compatible package manager
//! Update command

use anyhow::{Context, Result};
use stout_index::IndexSync;
use stout_state::{Config, Paths};
use clap::Args as ClapArgs;
use console::style;
use indicatif::{ProgressBar, ProgressStyle};

#[derive(ClapArgs)]
pub struct Args {
    /// Force update even if index is fresh
    #[arg(short, long)]
    pub force: bool,

    /// Skip signature verification (development only)
    #[arg(long, hide = true)]
    pub insecure: bool,
}

pub async fn run(args: Args) -> Result<()> {
    let paths = Paths::default();
    paths.ensure_dirs()?;

    let config = Config::load(&paths)?;

    // Use permissive security if --insecure flag is set (hidden, for dev only)
    let sync = if args.insecure {
        eprintln!("{}", style("WARNING: Running without signature verification").yellow().bold());
        IndexSync::permissive(Some(&config.index.base_url), &paths.stout_dir)?
    } else {
        IndexSync::with_security_policy(
            Some(&config.index.base_url),
            &paths.stout_dir,
            config.security.to_security_policy(),
        )?
    };

    // Show progress spinner
    let spinner = ProgressBar::new_spinner();
    spinner.set_style(
        ProgressStyle::default_spinner()
            .template("{spinner:.cyan} {msg}")
            .unwrap()
            .tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"),
    );
    spinner.set_message("Fetching index...");
    spinner.enable_steady_tick(std::time::Duration::from_millis(80));

    // Download index
    let manifest = sync
        .sync_index(paths.index_db())
        .await
        .context("Failed to sync index")?;

    spinner.finish_and_clear();

    // Show result
    println!(
        "\n{} to {} ({} formulas)",
        style("Updated").green().bold(),
        style(&manifest.version).cyan(),
        manifest.formula_count
    );

    // Show signature info
    if let Some(signed_at) = manifest.signed_at {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .as_secs();
        let age_hours = (now.saturating_sub(signed_at)) / 3600;
        if manifest.signature.is_some() {
            println!(
                "{} {}",
                style("✓ Signature verified").green(),
                style(format!("(signed {}h ago)", age_hours)).dim()
            );
        }
    }

    // Save manifest locally
    let manifest_json = serde_json::to_string_pretty(&manifest)?;
    std::fs::write(paths.manifest(), manifest_json)?;

    println!(
        "\n{}",
        style("Run 'stout search <query>' to find packages").dim()
    );

    Ok(())
}