vx_tool_rust/
rust_tool.rs

1//! Rust toolchain implementations with environment isolation
2
3use std::collections::HashMap;
4use vx_core::{
5    GitHubVersionParser, HttpUtils, Result, RustUrlBuilder, ToolContext, ToolExecutionResult,
6    VersionInfo, VxTool,
7};
8
9/// Macro to generate Rust tool implementations using VxTool trait
10macro_rules! rust_vx_tool {
11    ($name:ident, $cmd:literal, $desc:literal) => {
12        #[derive(Debug, Clone)]
13        pub struct $name {
14            _url_builder: RustUrlBuilder,
15            _version_parser: GitHubVersionParser,
16        }
17
18        impl $name {
19            pub fn new() -> Self {
20                Self {
21                    _url_builder: RustUrlBuilder::new(),
22                    _version_parser: GitHubVersionParser::new("rust-lang", "rust"),
23                }
24            }
25        }
26
27        #[async_trait::async_trait]
28        impl VxTool for $name {
29            fn name(&self) -> &str {
30                $cmd
31            }
32
33            fn description(&self) -> &str {
34                $desc
35            }
36
37            fn aliases(&self) -> Vec<&str> {
38                vec![]
39            }
40
41            async fn fetch_versions(&self, include_prerelease: bool) -> Result<Vec<VersionInfo>> {
42                // For Rust tools, fetch from GitHub releases
43                let json = HttpUtils::fetch_json(RustUrlBuilder::versions_url()).await?;
44                GitHubVersionParser::parse_versions(&json, include_prerelease)
45            }
46
47            async fn install_version(&self, version: &str, force: bool) -> Result<()> {
48                if !force && self.is_version_installed(version).await? {
49                    return Err(vx_core::VxError::VersionAlreadyInstalled {
50                        tool_name: self.name().to_string(),
51                        version: version.to_string(),
52                    });
53                }
54
55                let install_dir = self.get_version_install_dir(version);
56                let _exe_path = self.default_install_workflow(version, &install_dir).await?;
57
58                // Verify installation
59                if !self.is_version_installed(version).await? {
60                    return Err(vx_core::VxError::InstallationFailed {
61                        tool_name: self.name().to_string(),
62                        version: version.to_string(),
63                        message: "Installation verification failed".to_string(),
64                    });
65                }
66
67                Ok(())
68            }
69
70            async fn execute(
71                &self,
72                args: &[String],
73                context: &ToolContext,
74            ) -> Result<ToolExecutionResult> {
75                self.default_execute_workflow(args, context).await
76            }
77
78            async fn get_download_url(&self, version: &str) -> Result<Option<String>> {
79                let rust_url_builder = RustUrlBuilder::new();
80                Ok(rust_url_builder.download_url(version))
81            }
82
83            fn metadata(&self) -> HashMap<String, String> {
84                let mut meta = HashMap::new();
85                meta.insert(
86                    "homepage".to_string(),
87                    "https://www.rust-lang.org/".to_string(),
88                );
89                meta.insert("ecosystem".to_string(), "rust".to_string());
90                meta.insert(
91                    "repository".to_string(),
92                    "https://github.com/rust-lang/rust".to_string(),
93                );
94                meta.insert("license".to_string(), "MIT OR Apache-2.0".to_string());
95                meta
96            }
97        }
98
99        impl Default for $name {
100            fn default() -> Self {
101                Self::new()
102            }
103        }
104    };
105}
106
107// Define Rust tools using the VxTool macro
108rust_vx_tool!(CargoTool, "cargo", "Rust package manager and build tool");
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn test_cargo_tool_creation() {
116        let tool = CargoTool::new();
117        assert_eq!(tool.name(), "cargo");
118        assert!(!tool.description().is_empty());
119        assert!(tool.aliases().is_empty());
120    }
121
122    #[test]
123    fn test_rust_tool_metadata() {
124        let tool = CargoTool::new();
125        let metadata = tool.metadata();
126
127        assert!(metadata.contains_key("homepage"));
128        assert!(metadata.contains_key("ecosystem"));
129        assert_eq!(metadata.get("ecosystem"), Some(&"rust".to_string()));
130    }
131}