vx_cli/commands/
update.rs

1//! Update command implementation
2
3use crate::ui::UI;
4use vx_core::{PluginRegistry, Result, VxError};
5
6pub async fn handle(registry: &PluginRegistry, tool_name: Option<&str>, apply: bool) -> Result<()> {
7    match tool_name {
8        Some(tool_name) => {
9            // Update specific tool
10            update_single_tool(registry, tool_name, apply).await
11        }
12        None => {
13            // Update all tools
14            update_all_tools(registry, apply).await
15        }
16    }
17}
18
19async fn update_single_tool(registry: &PluginRegistry, tool_name: &str, apply: bool) -> Result<()> {
20    let tool = registry
21        .get_tool(tool_name)
22        .ok_or_else(|| VxError::ToolNotFound {
23            tool_name: tool_name.to_string(),
24        })?;
25
26    // Get installed versions
27    let installed_versions = tool.get_installed_versions().await?;
28    if installed_versions.is_empty() {
29        UI::warn(&format!("{} is not installed", tool_name));
30        UI::hint(&format!("Use 'vx install {}' to install it", tool_name));
31        return Ok(());
32    }
33
34    // Get latest version
35    UI::info(&format!("Checking for updates to {}...", tool_name));
36    let versions = tool.fetch_versions(false).await?;
37    if versions.is_empty() {
38        UI::warn(&format!("No versions found for {}", tool_name));
39        return Ok(());
40    }
41
42    let latest_version = &versions[0].version;
43    let current_version = &installed_versions[0]; // Most recent installed
44
45    if current_version == latest_version {
46        UI::success(&format!(
47            "{} {} is already up to date",
48            tool_name, current_version
49        ));
50        return Ok(());
51    }
52
53    UI::info(&format!(
54        "Update available: {} {} → {}",
55        tool_name, current_version, latest_version
56    ));
57
58    if !apply {
59        UI::hint("Use --apply to perform the update");
60        return Ok(());
61    }
62
63    // Perform the update
64    UI::info(&format!("Updating {} to {}...", tool_name, latest_version));
65
66    match tool.install_version(latest_version, false).await {
67        Ok(()) => {
68            UI::success(&format!(
69                "Successfully updated {} to {}",
70                tool_name, latest_version
71            ));
72        }
73        Err(e) => {
74            UI::error(&format!("Failed to update {}: {}", tool_name, e));
75            return Err(e);
76        }
77    }
78
79    Ok(())
80}
81
82async fn update_all_tools(registry: &PluginRegistry, apply: bool) -> Result<()> {
83    let tools = registry.get_all_tools();
84
85    if tools.is_empty() {
86        UI::warn("No tools available");
87        return Ok(());
88    }
89
90    UI::info("Checking for updates to all tools...");
91
92    let mut updates_available = Vec::new();
93    let mut errors = Vec::new();
94
95    for tool in tools {
96        match check_tool_update(&*tool).await {
97            Ok(Some((current, latest))) => {
98                updates_available.push((tool.name().to_string(), current, latest));
99            }
100            Ok(None) => {
101                // No update needed or not installed
102            }
103            Err(e) => {
104                UI::warn(&format!(
105                    "Failed to check updates for {}: {}",
106                    tool.name(),
107                    e
108                ));
109                errors.push((tool.name().to_string(), e));
110            }
111        }
112    }
113
114    if updates_available.is_empty() {
115        UI::success("All tools are up to date");
116        return Ok(());
117    }
118
119    UI::info(&format!(
120        "Found {} updates available:",
121        updates_available.len()
122    ));
123    for (tool_name, current, latest) in &updates_available {
124        UI::item(&format!("{}: {} → {}", tool_name, current, latest));
125    }
126
127    if !apply {
128        UI::hint("Use --apply to perform all updates");
129        return Ok(());
130    }
131
132    // Perform all updates
133    UI::info("Updating all tools...");
134
135    for (tool_name, _current, latest) in updates_available {
136        if let Some(tool) = registry.get_tool(&tool_name) {
137            match tool.install_version(&latest, false).await {
138                Ok(()) => {
139                    UI::success(&format!("Updated {} to {}", tool_name, latest));
140                }
141                Err(e) => {
142                    UI::error(&format!("Failed to update {}: {}", tool_name, e));
143                    errors.push((tool_name, e));
144                }
145            }
146        }
147    }
148
149    if errors.is_empty() {
150        UI::success("All tools updated successfully");
151    } else {
152        UI::warn(&format!("Some updates failed ({} errors)", errors.len()));
153    }
154
155    Ok(())
156}
157
158async fn check_tool_update(tool: &dyn vx_core::VxTool) -> Result<Option<(String, String)>> {
159    // Get installed versions
160    let installed_versions = tool.get_installed_versions().await?;
161    if installed_versions.is_empty() {
162        return Ok(None); // Not installed
163    }
164
165    // Get latest version
166    let versions = tool.fetch_versions(false).await?;
167    if versions.is_empty() {
168        return Ok(None); // No versions available
169    }
170
171    let current_version = &installed_versions[0];
172    let latest_version = &versions[0].version;
173
174    if current_version != latest_version {
175        Ok(Some((current_version.clone(), latest_version.clone())))
176    } else {
177        Ok(None) // Up to date
178    }
179}