Skip to main content

chub_cli/commands/
update.rs

1use clap::Args;
2use indicatif::{ProgressBar, ProgressStyle};
3use owo_colors::OwoColorize;
4
5use chub_core::cache::evict_lru_cache;
6use chub_core::config::load_config;
7use chub_core::error::Result;
8use chub_core::fetch::{fetch_all_registries, fetch_full_bundle};
9
10use crate::output;
11
12#[derive(Args)]
13pub struct UpdateArgs {
14    /// Force re-download even if cache is fresh
15    #[arg(long)]
16    force: bool,
17
18    /// Download the full bundle for offline use
19    #[arg(long)]
20    full: bool,
21}
22
23pub async fn run(args: UpdateArgs, json: bool) -> Result<()> {
24    let config = load_config();
25
26    if args.full {
27        let remote_count = config.sources.iter().filter(|s| s.path.is_none()).count();
28        let pb = if !json && remote_count > 0 {
29            let pb = ProgressBar::new(remote_count as u64);
30            pb.set_style(
31                ProgressStyle::with_template("{spinner:.cyan} [{bar:20}] {pos}/{len} {msg}")
32                    .unwrap()
33                    .tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏-"),
34            );
35            Some(pb)
36        } else {
37            None
38        };
39
40        for source in &config.sources {
41            if source.path.is_some() {
42                if !json {
43                    output::info(&format!("Skipping local source: {}", source.name));
44                }
45                continue;
46            }
47            if let Some(ref pb) = pb {
48                pb.set_message(format!("Downloading {}...", source.name));
49            }
50            fetch_full_bundle(&source.name).await?;
51            if let Some(ref pb) = pb {
52                pb.inc(1);
53            }
54        }
55
56        if let Some(pb) = pb {
57            pb.finish_and_clear();
58        }
59
60        // Run LRU eviction after downloading bundles
61        let freed = evict_lru_cache(None);
62
63        if json {
64            println!("{}", serde_json::json!({ "status": "ok", "mode": "full" }));
65        } else {
66            output::success("Full bundle(s) downloaded and extracted.");
67            if freed > 0 {
68                output::info(&format!("Cache eviction freed {} KB.", freed / 1024));
69            }
70        }
71    } else {
72        let pb = if !json {
73            let pb = ProgressBar::new_spinner();
74            pb.set_style(
75                ProgressStyle::with_template("{spinner:.cyan} {msg}")
76                    .unwrap()
77                    .tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏-"),
78            );
79            pb.set_message("Updating registries...");
80            pb.enable_steady_tick(std::time::Duration::from_millis(80));
81            Some(pb)
82        } else {
83            None
84        };
85
86        let errors = fetch_all_registries(true).await;
87
88        if let Some(pb) = pb {
89            pb.finish_and_clear();
90        }
91
92        for e in &errors {
93            eprintln!("{}", format!("Warning: {}: {}", e.source, e.error).yellow());
94        }
95
96        let updated = config
97            .sources
98            .iter()
99            .filter(|s| s.path.is_none())
100            .count()
101            .saturating_sub(errors.len());
102
103        if json {
104            println!(
105                "{}",
106                serde_json::json!({
107                    "status": "ok",
108                    "mode": "registry",
109                    "updated": updated,
110                    "errors": errors,
111                })
112            );
113        } else {
114            output::success(&format!("Registry updated ({} remote source(s)).", updated));
115        }
116    }
117
118    Ok(())
119}