vx_core/
tool.rs

1//! Four-layer tool architecture with environment isolation and configuration management
2
3use crate::{Result, VersionInfo};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::path::{Path, PathBuf};
7
8/// Core async tool interface - all tools are environment-isolated with version control
9#[async_trait::async_trait]
10pub trait AsyncTool: Send + Sync {
11    /// Get the name of this tool
12    fn name(&self) -> &str;
13
14    /// Get tool description
15    fn description(&self) -> &str {
16        "A development tool"
17    }
18
19    /// Get supported aliases for this tool
20    fn aliases(&self) -> Vec<&str> {
21        vec![]
22    }
23
24    /// Fetch available versions from the tool's official source
25    async fn fetch_versions(&self, include_prerelease: bool) -> Result<Vec<VersionInfo>>;
26
27    /// Install a specific version of the tool
28    async fn install_version(&self, version: &str) -> Result<PathBuf>;
29
30    /// Get all installed versions
31    async fn get_installed_versions(&self) -> Result<Vec<String>>;
32
33    /// Uninstall a specific version
34    async fn uninstall_version(&self, version: &str) -> Result<()>;
35
36    /// Get download URL for a specific version
37    async fn get_download_url(&self, version: &str) -> Result<Option<String>>;
38
39    /// Get the executable path for a specific version (vx-managed, environment-isolated)
40    fn get_executable_path(&self, version: &str) -> PathBuf;
41
42    /// Execute the tool with given context
43    async fn execute(&self, context: &ToolContext) -> Result<i32>;
44
45    /// Check if a specific version is installed
46    async fn is_version_installed(&self, version: &str) -> Result<bool> {
47        let installed_versions = self.get_installed_versions().await?;
48        Ok(installed_versions.contains(&version.to_string()))
49    }
50
51    /// Get the base installation directory for this tool
52    fn get_base_install_dir(&self) -> PathBuf {
53        dirs::home_dir()
54            .unwrap_or_else(|| PathBuf::from("."))
55            .join(".vx")
56            .join("tools")
57            .join(self.name())
58    }
59
60    /// Get installation directory for a specific version
61    fn get_version_install_dir(&self, version: &str) -> PathBuf {
62        self.get_base_install_dir().join(version)
63    }
64}
65
66/// Virtual environment management interface
67pub trait Environment: Send + Sync {
68    /// Get environment name
69    fn name(&self) -> &str;
70
71    /// Get tool version for this environment
72    fn get_tool_version(&self, tool_name: &str) -> Option<String>;
73
74    /// Set tool version for this environment
75    fn set_tool_version(&mut self, tool_name: &str, version: String) -> Result<()>;
76
77    /// Get environment directory path
78    fn get_environment_path(&self) -> PathBuf;
79
80    /// Activate this environment and return tool context
81    fn activate(&self) -> Result<ToolContext>;
82
83    /// List all tools configured in this environment
84    fn list_tools(&self) -> Result<HashMap<String, String>>;
85}
86
87/// Configuration management interface
88pub trait Configuration: Send + Sync {
89    /// Get global tool version
90    fn get_global_tool_version(&self, tool_name: &str) -> Option<String>;
91
92    /// Set global tool version
93    fn set_global_tool_version(&self, tool_name: &str, version: String) -> Result<()>;
94
95    /// Get project-specific tool version
96    fn get_project_tool_version(&self, tool_name: &str, project_path: &Path) -> Option<String>;
97
98    /// Set project-specific tool version
99    fn set_project_tool_version(
100        &self,
101        tool_name: &str,
102        version: String,
103        project_path: &Path,
104    ) -> Result<()>;
105
106    /// Get active environment name
107    fn get_active_environment(&self) -> Option<String>;
108
109    /// Set active environment
110    fn set_active_environment(&self, env_name: Option<String>) -> Result<()>;
111
112    /// Resolve tool version based on priority: CLI > Environment > Project > Global > Latest
113    fn resolve_tool_version(
114        &self,
115        tool_name: &str,
116        cli_version: Option<&str>,
117        project_path: Option<&Path>,
118    ) -> Option<String>;
119}
120
121/// Plugin interface - organizes related tools
122pub trait Plugin: Send + Sync {
123    /// Plugin name
124    fn name(&self) -> &str;
125
126    /// Plugin description
127    fn description(&self) -> &str {
128        "A vx plugin"
129    }
130
131    /// Plugin version
132    fn version(&self) -> &str {
133        "0.1.0"
134    }
135
136    /// Get all tools provided by this plugin
137    fn tools(&self) -> Vec<Box<dyn AsyncTool>>;
138
139    /// Check if this plugin supports a specific tool
140    fn supports_tool(&self, tool_name: &str) -> bool;
141}
142
143/// Tool execution context with environment isolation
144#[derive(Debug, Clone)]
145pub struct ToolContext {
146    pub tool_name: String,
147    pub version: String,
148    pub args: Vec<String>,
149    pub working_directory: Option<PathBuf>,
150    pub environment_variables: HashMap<String, String>,
151    pub environment: Option<String>, // venv name
152    pub use_system_path: bool,       // Whether to use system PATH or vx-managed tools
153}
154
155impl ToolContext {
156    pub fn new(tool_name: String, version: String, args: Vec<String>) -> Self {
157        Self {
158            tool_name,
159            version,
160            args,
161            working_directory: None,
162            environment_variables: HashMap::new(),
163            environment: None,
164            use_system_path: false,
165        }
166    }
167}
168
169/// Tool execution result
170#[derive(Debug)]
171pub struct ToolExecutionResult {
172    pub exit_code: i32,
173    pub stdout: Option<String>,
174    pub stderr: Option<String>,
175}
176
177/// Information about a tool
178#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct ToolInfo {
180    pub name: String,
181    pub description: String,
182    pub aliases: Vec<String>,
183    pub installed_versions: Vec<String>,
184    pub current_version: Option<String>,
185    pub latest_version: Option<String>,
186}
187
188/// Status of a tool in a specific environment
189#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct ToolStatus {
191    pub installed: bool,
192    pub current_version: Option<String>,
193    pub installed_versions: Vec<String>,
194}
195
196/// Synchronous tool interface for compatibility with legacy code
197/// This trait provides a synchronous interface for tools that don't need async operations
198pub trait Tool: Send + Sync {
199    /// Get the tool name
200    fn name(&self) -> &str;
201
202    /// Get tool description
203    fn description(&self) -> &str {
204        "A development tool"
205    }
206
207    /// Get tool homepage URL
208    fn homepage(&self) -> Option<&str> {
209        None
210    }
211
212    /// Check if the tool is installed on the system
213    fn is_installed(&self) -> Result<bool>;
214
215    /// Get the currently installed version (if any)
216    fn get_version(&self) -> Result<Option<String>>;
217
218    /// Get the expected executable path for a given version
219    fn get_executable_path(&self, version: &str, install_dir: &Path) -> PathBuf;
220
221    /// Execute the tool with given arguments
222    fn execute(&self, args: &[String]) -> Result<i32>;
223
224    /// Check if tool supports auto-installation
225    fn supports_auto_install(&self) -> bool {
226        true
227    }
228
229    /// Get tool information
230    fn get_info(&self) -> ToolInfo {
231        ToolInfo {
232            name: self.name().to_string(),
233            description: self.description().to_string(),
234            aliases: vec![],
235            installed_versions: vec![],
236            current_version: self.get_version().unwrap_or_default(),
237            latest_version: None,
238        }
239    }
240}