vx_cli/commands/
install.rs1use crate::ui::UI;
4use anyhow::Result;
5use tracing::{info_span, Instrument};
6use vx_plugin::PluginRegistry;
7
8pub async fn handle(
9 registry: &PluginRegistry,
10 tool_name: &str,
11 version: Option<&str>,
12 force: bool,
13) -> Result<()> {
14 let tool = registry
16 .get_tool(tool_name)
17 .ok_or_else(|| anyhow::anyhow!("Tool not found: {}", tool_name))?;
18
19 let target_version = if let Some(v) = version {
21 v.to_string()
22 } else {
23 let span = info_span!("Fetching latest version", tool = tool_name);
25 let versions = async {
26 UI::info(&format!("Fetching latest version for {}...", tool_name));
27 tool.fetch_versions(false).await
28 }
29 .instrument(span)
30 .await?;
31
32 if versions.is_empty() {
33 return Err(anyhow::anyhow!("No versions found for tool: {}", tool_name));
34 }
35 versions[0].version.clone()
36 };
37
38 UI::info(&format!("Installing {} {}...", tool_name, target_version));
39
40 if !force && tool.is_version_installed(&target_version).await? {
42 UI::success(&format!(
43 "{} {} is already installed",
44 tool_name, target_version
45 ));
46 UI::hint("Use --force to reinstall");
47 return Ok(());
48 }
49
50 let install_span = info_span!("Installing tool", tool = tool_name, version = %target_version);
52 let install_result = async { tool.install_version(&target_version, force).await }
53 .instrument(install_span)
54 .await;
55
56 match install_result {
57 Ok(()) => {
58 UI::success(&format!(
59 "Successfully installed {} {}",
60 tool_name, target_version
61 ));
62
63 let install_dir = tool.get_version_install_dir(&target_version);
65 UI::detail(&format!("Installed to: {}", install_dir.display()));
66
67 UI::hint(&format!(
69 "Use 'vx {} --version' to verify installation",
70 tool_name
71 ));
72 }
73 Err(e) => {
74 UI::error(&format!(
75 "Failed to install {} {}: {}",
76 tool_name, target_version, e
77 ));
78 return Err(e);
79 }
80 }
81
82 Ok(())
83}