chub_cli/commands/
update.rs1use 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 #[arg(long)]
16 force: bool,
17
18 #[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 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}