vx_cli/commands/
switch.rs

1//! Switch command implementation
2
3use crate::ui::UI;
4use vx_core::{PluginRegistry, Result, VxEnvironment, VxError, VxShimManager};
5
6pub async fn handle(_registry: &PluginRegistry, tool_version: &str, global: bool) -> Result<()> {
7    // Parse tool@version format
8    let (tool_name, version) = parse_tool_version(tool_version)?;
9
10    UI::info(&format!("Switching {} to version {}", tool_name, version));
11
12    // Create environment and shim manager
13    let environment = VxEnvironment::new()?;
14    let shim_manager = VxShimManager::new(environment.clone())?;
15
16    // Check if the version is installed
17    if !environment.is_version_installed(&tool_name, &version) {
18        UI::error(&format!(
19            "Version {} of {} is not installed. Install it first with: vx install {}@{}",
20            version, tool_name, tool_name, version
21        ));
22        return Err(VxError::VersionNotInstalled {
23            tool_name: tool_name.clone(),
24            version: version.clone(),
25        });
26    }
27
28    // Get the installation info to find the executable path
29    let installation = environment
30        .get_installation_info(&tool_name, &version)?
31        .ok_or_else(|| VxError::VersionNotInstalled {
32            tool_name: tool_name.clone(),
33            version: version.clone(),
34        })?;
35
36    // Switch the tool version using shim
37    shim_manager.switch_tool_version(&tool_name, &version, &installation.executable_path)?;
38
39    if global {
40        // Set as global default version
41        environment.set_active_version(&tool_name, &version)?;
42        UI::success(&format!(
43            "Switched {} to version {} globally",
44            tool_name, version
45        ));
46        UI::hint(&format!(
47            "All new terminal sessions will use {}@{}",
48            tool_name, version
49        ));
50    } else {
51        // Session-level switch (for now, just update the shim)
52        UI::success(&format!(
53            "Switched {} to version {} in current session",
54            tool_name, version
55        ));
56        UI::hint(&format!(
57            "Use 'vx switch {}@{} --global' to make this the default for all sessions",
58            tool_name, version
59        ));
60    }
61
62    // Verify the switch worked
63    let shim_dir = environment.shim_dir()?;
64    let shim_path = shim_dir.join(if cfg!(windows) {
65        format!("{}.exe", tool_name)
66    } else {
67        tool_name.clone()
68    });
69
70    if shim_path.exists() {
71        UI::info(&format!("Shim created at: {}", shim_path.display()));
72        UI::hint(&format!(
73            "Make sure {} is in your PATH to use the switched version",
74            shim_dir.display()
75        ));
76    }
77
78    Ok(())
79}
80
81/// Parse tool@version format
82pub fn parse_tool_version(tool_version: &str) -> Result<(String, String)> {
83    if let Some((tool, version)) = tool_version.split_once('@') {
84        if tool.is_empty() || version.is_empty() {
85            return Err(VxError::ParseError {
86                message: format!("Invalid tool@version format: {}", tool_version),
87            });
88        }
89        Ok((tool.to_string(), version.to_string()))
90    } else {
91        Err(VxError::ParseError {
92            message: format!(
93                "Invalid format: {}. Expected format: tool@version (e.g., node@20.10.0)",
94                tool_version
95            ),
96        })
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_parse_tool_version() {
106        // Valid cases
107        assert_eq!(
108            parse_tool_version("node@20.10.0").unwrap(),
109            ("node".to_string(), "20.10.0".to_string())
110        );
111        assert_eq!(
112            parse_tool_version("python@3.11.0").unwrap(),
113            ("python".to_string(), "3.11.0".to_string())
114        );
115
116        // Invalid cases
117        assert!(parse_tool_version("node").is_err());
118        assert!(parse_tool_version("@20.10.0").is_err());
119        assert!(parse_tool_version("node@").is_err());
120        assert!(parse_tool_version("").is_err());
121    }
122}