vx_core/
core.rs

1//! # vx-core - Core abstractions and interfaces
2//!
3//! This module provides the essential abstractions for the vx tool ecosystem.
4//! Following SOLID principles, it defines interfaces without implementations.
5
6use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::path::PathBuf;
10
11/// Core result type for vx operations
12pub type VxResult<T> = Result<T, VxError>;
13
14/// Core error types for vx operations
15#[derive(thiserror::Error, Debug)]
16pub enum VxError {
17    #[error("Tool '{tool}' not found")]
18    ToolNotFound { tool: String },
19
20    #[error("Version '{version}' not found for tool '{tool}'")]
21    VersionNotFound { tool: String, version: String },
22
23    #[error("Installation failed for '{tool}': {reason}")]
24    InstallationFailed { tool: String, reason: String },
25
26    #[error("Execution failed: {message}")]
27    ExecutionFailed { message: String },
28
29    #[error("Configuration error: {message}")]
30    ConfigError { message: String },
31
32    #[error("IO error: {0}")]
33    Io(#[from] std::io::Error),
34
35    #[error("Other error: {0}")]
36    Other(#[from] anyhow::Error),
37}
38
39/// Platform information
40#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
41pub struct Platform {
42    /// Operating system (windows, macos, linux)
43    pub os: String,
44    /// Architecture (x86_64, aarch64, etc.)
45    pub arch: String,
46}
47
48impl Platform {
49    /// Get current platform
50    pub fn current() -> Self {
51        Self {
52            os: std::env::consts::OS.to_string(),
53            arch: std::env::consts::ARCH.to_string(),
54        }
55    }
56}
57
58impl std::fmt::Display for Platform {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        let platform_str = match (self.os.as_str(), self.arch.as_str()) {
61            ("windows", "x86_64") => "win-x64".to_string(),
62            ("windows", "aarch64") => "win-arm64".to_string(),
63            ("macos", "x86_64") => "darwin-x64".to_string(),
64            ("macos", "aarch64") => "darwin-arm64".to_string(),
65            ("linux", "x86_64") => "linux-x64".to_string(),
66            ("linux", "aarch64") => "linux-arm64".to_string(),
67            _ => format!("{}-{}", self.os, self.arch),
68        };
69        write!(f, "{}", platform_str)
70    }
71}
72
73/// Version information
74#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
75pub struct Version {
76    /// Version string
77    pub version: String,
78    /// Whether this is a prerelease
79    pub prerelease: bool,
80    /// Additional metadata
81    pub metadata: HashMap<String, String>,
82}
83
84impl Version {
85    /// Create a new version
86    pub fn new(version: impl Into<String>) -> Self {
87        Self {
88            version: version.into(),
89            prerelease: false,
90            metadata: HashMap::new(),
91        }
92    }
93
94    /// Create a prerelease version
95    pub fn prerelease(version: impl Into<String>) -> Self {
96        Self {
97            version: version.into(),
98            prerelease: true,
99            metadata: HashMap::new(),
100        }
101    }
102}
103
104/// Tool specification
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct ToolSpec {
107    /// Tool name
108    pub name: String,
109    /// Tool description
110    pub description: String,
111    /// Supported platforms
112    pub platforms: Vec<Platform>,
113    /// Available versions
114    pub versions: Vec<Version>,
115    /// Installation methods
116    pub install_methods: Vec<String>,
117    /// Dependencies
118    pub dependencies: Vec<String>,
119}
120
121/// Tool installation configuration
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct InstallConfig {
124    /// Tool name
125    pub tool: String,
126    /// Version to install
127    pub version: String,
128    /// Target platform
129    pub platform: Platform,
130    /// Installation directory
131    pub install_dir: PathBuf,
132    /// Download URL
133    pub download_url: Option<String>,
134    /// Installation method
135    pub method: InstallMethod,
136}
137
138/// Installation methods
139#[derive(Debug, Clone, Serialize, Deserialize)]
140pub enum InstallMethod {
141    /// Download and extract archive
142    Archive { format: ArchiveFormat },
143    /// Download binary directly
144    Binary,
145    /// Use package manager
146    PackageManager { manager: String },
147    /// Custom installation script
148    Custom { script: String },
149}
150
151/// Archive formats
152#[derive(Debug, Clone, Serialize, Deserialize)]
153pub enum ArchiveFormat {
154    Zip,
155    TarGz,
156    TarXz,
157}
158
159/// Execution context for tools
160#[derive(Debug, Clone)]
161pub struct ExecutionContext {
162    /// Working directory
163    pub working_dir: PathBuf,
164    /// Environment variables
165    pub env_vars: HashMap<String, String>,
166    /// Tool arguments
167    pub args: Vec<String>,
168}
169
170impl Default for ExecutionContext {
171    fn default() -> Self {
172        Self {
173            working_dir: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
174            env_vars: HashMap::new(),
175            args: Vec::new(),
176        }
177    }
178}
179
180/// Execution result
181#[derive(Debug)]
182pub struct ExecutionResult {
183    /// Exit code
184    pub exit_code: i32,
185    /// Execution time
186    pub duration: std::time::Duration,
187    /// Whether the execution was successful
188    pub success: bool,
189}
190
191/// Core trait for tool management
192#[async_trait]
193pub trait ToolManager: Send + Sync {
194    /// Check if a tool is available
195    async fn is_available(&self, tool: &str) -> VxResult<bool>;
196
197    /// Get installed version of a tool
198    async fn get_version(&self, tool: &str) -> VxResult<Option<Version>>;
199
200    /// Install a tool
201    async fn install(&self, config: &InstallConfig) -> VxResult<()>;
202
203    /// Execute a tool
204    async fn execute(&self, tool: &str, context: &ExecutionContext) -> VxResult<ExecutionResult>;
205
206    /// List available tools
207    async fn list_tools(&self) -> VxResult<Vec<String>>;
208}
209
210/// Core trait for tool resolution
211#[async_trait]
212pub trait ToolResolver: Send + Sync {
213    /// Resolve tool specification
214    async fn resolve(&self, tool: &str) -> VxResult<ToolSpec>;
215
216    /// Get installation configuration
217    async fn get_install_config(&self, tool: &str, version: &str) -> VxResult<InstallConfig>;
218}
219
220/// Core trait for version management
221#[async_trait]
222pub trait VersionManager: Send + Sync {
223    /// List available versions for a tool
224    async fn list_versions(&self, tool: &str) -> VxResult<Vec<Version>>;
225
226    /// Get latest version for a tool
227    async fn get_latest(&self, tool: &str) -> VxResult<Version>;
228
229    /// Check if a version satisfies a constraint
230    fn satisfies(&self, version: &Version, constraint: &str) -> bool;
231}
232
233/// Configuration for vx operations
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct VxConfig {
236    /// Installation directory
237    pub install_dir: PathBuf,
238    /// Cache directory
239    pub cache_dir: PathBuf,
240    /// Default platform
241    pub platform: Platform,
242    /// Registry URLs
243    pub registries: Vec<String>,
244    /// Tool-specific configurations
245    pub tools: HashMap<String, serde_json::Value>,
246}
247
248impl Default for VxConfig {
249    fn default() -> Self {
250        let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
251        let vx_dir = home_dir.join(".vx");
252
253        Self {
254            install_dir: vx_dir.join("tools"),
255            cache_dir: vx_dir.join("cache"),
256            platform: Platform::current(),
257            registries: vec!["https://registry.vx.dev".to_string()],
258            tools: HashMap::new(),
259        }
260    }
261}
262
263#[cfg(test)]
264mod tests {
265    use super::*;
266
267    #[test]
268    fn test_platform_current() {
269        let platform = Platform::current();
270        assert!(!platform.os.is_empty());
271        assert!(!platform.arch.is_empty());
272    }
273
274    #[test]
275    fn test_platform_to_string() {
276        let platform = Platform {
277            os: "linux".to_string(),
278            arch: "x86_64".to_string(),
279        };
280        assert_eq!(platform.to_string(), "linux-x64");
281    }
282
283    #[test]
284    fn test_version_creation() {
285        let version = Version::new("1.0.0");
286        assert_eq!(version.version, "1.0.0");
287        assert!(!version.prerelease);
288
289        let prerelease = Version::prerelease("2.0.0-beta.1");
290        assert!(prerelease.prerelease);
291    }
292
293    #[test]
294    fn test_vx_config_default() {
295        let config = VxConfig::default();
296        assert!(config.install_dir.to_string_lossy().contains(".vx"));
297        assert!(!config.registries.is_empty());
298    }
299}