-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmanager.rs
More file actions
225 lines (199 loc) · 6.86 KB
/
Copy pathmanager.rs
File metadata and controls
225 lines (199 loc) · 6.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
//! Dependency management and detection coordination
//!
//! This module provides the main dependency manager that coordinates detection
//! operations for all supported dependencies.
// Standard library
use std::fmt;
use std::str::FromStr;
// Internal crate
use crate::detector::{
AnsibleDetector, CargoMacheteDetector, DependencyDetector, DetectionError, LxdDetector,
OpenTofuDetector,
};
use crate::installer::{
AnsibleInstaller, CargoMacheteInstaller, DependencyInstaller, InstallationError, LxdInstaller,
OpenTofuInstaller,
};
// ============================================================================
// PUBLIC API - Main Types
// ============================================================================
/// Enum representing available dependencies
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Dependency {
CargoMachete,
OpenTofu,
Ansible,
Lxd,
}
/// Result of checking a single dependency
#[derive(Debug, Clone)]
pub struct CheckResult {
/// The dependency that was checked
pub dependency: Dependency,
/// Whether the dependency is installed
pub installed: bool,
}
/// Result of installing a single dependency
#[derive(Debug, Clone)]
pub struct InstallResult {
/// The dependency that was installed
pub dependency: Dependency,
/// Whether the installation succeeded
pub success: bool,
/// Error message if installation failed
pub error: Option<String>,
}
/// Main dependency manager for detection operations
pub struct DependencyManager;
// ============================================================================
// PUBLIC API - Implementations
// ============================================================================
impl Dependency {
/// Returns all available dependencies
#[must_use]
pub const fn all() -> &'static [Self] {
&[Self::CargoMachete, Self::OpenTofu, Self::Ansible, Self::Lxd]
}
/// Returns the canonical name for this dependency
#[must_use]
pub const fn canonical_name(&self) -> &'static str {
match self {
Self::CargoMachete => "cargo-machete",
Self::OpenTofu => "opentofu",
Self::Ansible => "ansible",
Self::Lxd => "lxd",
}
}
}
impl fmt::Display for Dependency {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.canonical_name())
}
}
impl FromStr for Dependency {
type Err = DependencyParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"cargo-machete" | "machete" => Ok(Self::CargoMachete),
"opentofu" | "tofu" => Ok(Self::OpenTofu),
"ansible" => Ok(Self::Ansible),
"lxd" => Ok(Self::Lxd),
_ => Err(DependencyParseError::UnknownDependency {
name: s.to_string(),
}),
}
}
}
impl DependencyManager {
/// Create a new dependency manager
#[must_use]
pub const fn new() -> Self {
Self
}
/// Check all dependencies and return results
///
/// # Errors
///
/// Returns an error if any detection operation fails
pub fn check_all(&self) -> Result<Vec<CheckResult>, DetectionError> {
Dependency::all()
.iter()
.map(|&dependency| {
let detector = self.get_detector(dependency);
let installed = detector.is_installed()?;
Ok(CheckResult {
dependency,
installed,
})
})
.collect()
}
/// Get a specific detector by dependency type
///
/// Note: This creates a new detector instance on each call, which is acceptable
/// since detectors are lightweight and stateless.
#[must_use]
pub fn get_detector(&self, dep: Dependency) -> Box<dyn DependencyDetector> {
match dep {
Dependency::CargoMachete => Box::new(CargoMacheteDetector),
Dependency::OpenTofu => Box::new(OpenTofuDetector),
Dependency::Ansible => Box::new(AnsibleDetector),
Dependency::Lxd => Box::new(LxdDetector),
}
}
/// Get a specific installer by dependency type
///
/// Note: This creates a new installer instance on each call, which is acceptable
/// since installers are lightweight and stateless.
#[must_use]
pub fn get_installer(&self, dep: Dependency) -> Box<dyn DependencyInstaller> {
match dep {
Dependency::CargoMachete => Box::new(CargoMacheteInstaller),
Dependency::OpenTofu => Box::new(OpenTofuInstaller),
Dependency::Ansible => Box::new(AnsibleInstaller),
Dependency::Lxd => Box::new(LxdInstaller),
}
}
/// Install a specific dependency
///
/// # Errors
///
/// Returns an error if the installation process fails
pub async fn install(&self, dep: Dependency) -> Result<(), InstallationError> {
let installer = self.get_installer(dep);
installer.install().await
}
/// Install all dependencies and return results
///
/// This method attempts to install all dependencies, collecting results
/// for each one. Even if some installations fail, all dependencies will
/// be attempted.
pub async fn install_all(&self) -> Vec<InstallResult> {
let mut results = Vec::new();
for &dependency in Dependency::all() {
let result = match self.install(dependency).await {
Ok(()) => InstallResult {
dependency,
success: true,
error: None,
},
Err(e) => InstallResult {
dependency,
success: false,
error: Some(e.to_string()),
},
};
results.push(result);
}
results
}
}
impl Default for DependencyManager {
fn default() -> Self {
Self::new()
}
}
// ============================================================================
// ERROR TYPES - Secondary Concerns
// ============================================================================
/// Error that occurs when parsing a dependency name
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DependencyParseError {
/// Unknown dependency name provided
UnknownDependency { name: String },
}
impl fmt::Display for DependencyParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnknownDependency { name } => {
let available = Dependency::all()
.iter()
.map(Dependency::canonical_name)
.collect::<Vec<_>>()
.join(", ");
write!(f, "Unknown dependency: {name}. Available: {available}")
}
}
}
}
impl std::error::Error for DependencyParseError {}