vx_dependency/
types.rs

1//! Core types for dependency resolution
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Specification for a tool and its dependencies
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ToolSpec {
9    /// Tool name
10    pub name: String,
11    /// Tool version (if specific version is required)
12    pub version: Option<String>,
13    /// List of dependencies
14    pub dependencies: Vec<DependencySpec>,
15    /// Tool metadata
16    pub metadata: HashMap<String, String>,
17    /// Whether this tool supports auto-installation
18    pub auto_installable: bool,
19    /// Installation priority (higher = install first)
20    pub priority: i32,
21}
22
23/// Specification for a dependency
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct DependencySpec {
26    /// Name of the dependency tool
27    pub tool_name: String,
28    /// Version constraint
29    pub version_constraint: Option<VersionConstraint>,
30    /// Dependency type
31    pub dependency_type: DependencyType,
32    /// Human-readable description
33    pub description: String,
34    /// Whether this dependency is optional
35    pub optional: bool,
36    /// Platform-specific constraints
37    pub platforms: Vec<String>,
38}
39
40/// Type of dependency relationship
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
42pub enum DependencyType {
43    /// Required at runtime
44    Runtime,
45    /// Required for building/compilation
46    Build,
47    /// Required for development
48    Development,
49    /// Peer dependency (should be provided by user)
50    Peer,
51    /// Optional enhancement
52    Optional,
53}
54
55/// Version constraint specification
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct VersionConstraint {
58    /// Constraint expression (e.g., ">=1.0.0", "^2.1.0", "~1.2.3")
59    pub expression: String,
60    /// Whether to allow prerelease versions
61    pub allow_prerelease: bool,
62}
63
64impl Default for ToolSpec {
65    fn default() -> Self {
66        Self {
67            name: String::new(),
68            version: None,
69            dependencies: Vec::new(),
70            metadata: HashMap::new(),
71            auto_installable: true,
72            priority: 0,
73        }
74    }
75}
76
77impl DependencySpec {
78    /// Create a required runtime dependency
79    pub fn required(tool_name: impl Into<String>, description: impl Into<String>) -> Self {
80        Self {
81            tool_name: tool_name.into(),
82            version_constraint: None,
83            dependency_type: DependencyType::Runtime,
84            description: description.into(),
85            optional: false,
86            platforms: vec![],
87        }
88    }
89
90    /// Create an optional dependency
91    pub fn optional(tool_name: impl Into<String>, description: impl Into<String>) -> Self {
92        Self {
93            tool_name: tool_name.into(),
94            version_constraint: None,
95            dependency_type: DependencyType::Optional,
96            description: description.into(),
97            optional: true,
98            platforms: vec![],
99        }
100    }
101
102    /// Create a build-time dependency
103    pub fn build(tool_name: impl Into<String>, description: impl Into<String>) -> Self {
104        Self {
105            tool_name: tool_name.into(),
106            version_constraint: None,
107            dependency_type: DependencyType::Build,
108            description: description.into(),
109            optional: false,
110            platforms: vec![],
111        }
112    }
113
114    /// Set version constraint
115    pub fn with_version(mut self, constraint: impl Into<String>) -> Self {
116        self.version_constraint = Some(VersionConstraint {
117            expression: constraint.into(),
118            allow_prerelease: false,
119        });
120        self
121    }
122
123    /// Set version constraint with prerelease support
124    pub fn with_version_prerelease(mut self, constraint: impl Into<String>) -> Self {
125        self.version_constraint = Some(VersionConstraint {
126            expression: constraint.into(),
127            allow_prerelease: true,
128        });
129        self
130    }
131
132    /// Set platform constraints
133    pub fn for_platforms(mut self, platforms: Vec<String>) -> Self {
134        self.platforms = platforms;
135        self
136    }
137
138    /// Check if this dependency applies to the current platform
139    pub fn applies_to_platform(&self, platform: &str) -> bool {
140        self.platforms.is_empty() || self.platforms.contains(&platform.to_string())
141    }
142}
143
144impl VersionConstraint {
145    /// Create a new version constraint
146    pub fn new(expression: impl Into<String>) -> Self {
147        Self {
148            expression: expression.into(),
149            allow_prerelease: false,
150        }
151    }
152
153    /// Create a version constraint that allows prerelease versions
154    pub fn with_prerelease(expression: impl Into<String>) -> Self {
155        Self {
156            expression: expression.into(),
157            allow_prerelease: true,
158        }
159    }
160
161    /// Check if this constraint is satisfied by a version
162    pub fn is_satisfied_by(&self, version: &str) -> bool {
163        // TODO: Implement proper semantic version matching
164        // For now, simple string comparison
165        !version.is_empty()
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172
173    #[test]
174    fn test_dependency_spec_creation() {
175        let dep = DependencySpec::required("node", "Node.js runtime")
176            .with_version(">=16.0.0")
177            .for_platforms(vec!["linux".to_string(), "macos".to_string()]);
178
179        assert_eq!(dep.tool_name, "node");
180        assert_eq!(dep.dependency_type, DependencyType::Runtime);
181        assert!(!dep.optional);
182        assert!(dep.applies_to_platform("linux"));
183        assert!(!dep.applies_to_platform("windows"));
184    }
185
186    #[test]
187    fn test_tool_spec_default() {
188        let tool = ToolSpec::default();
189        assert!(tool.name.is_empty());
190        assert!(tool.dependencies.is_empty());
191        assert!(tool.auto_installable);
192        assert_eq!(tool.priority, 0);
193    }
194
195    #[test]
196    fn test_version_constraint() {
197        let constraint = VersionConstraint::new(">=1.0.0");
198        assert_eq!(constraint.expression, ">=1.0.0");
199        assert!(!constraint.allow_prerelease);
200
201        let constraint_pre = VersionConstraint::with_prerelease("^2.0.0-beta");
202        assert!(constraint_pre.allow_prerelease);
203    }
204
205    #[test]
206    fn test_dependency_types() {
207        let runtime_dep = DependencySpec::required("node", "Runtime dependency");
208        assert_eq!(runtime_dep.dependency_type, DependencyType::Runtime);
209
210        let build_dep = DependencySpec::build("gcc", "Build dependency");
211        assert_eq!(build_dep.dependency_type, DependencyType::Build);
212
213        let optional_dep = DependencySpec::optional("docker", "Optional dependency");
214        assert_eq!(optional_dep.dependency_type, DependencyType::Optional);
215        assert!(optional_dep.optional);
216    }
217}