-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathverification.rs
More file actions
159 lines (144 loc) · 5.65 KB
/
Copy pathverification.rs
File metadata and controls
159 lines (144 loc) · 5.65 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
//! Dependency Verification
//!
//! This module provides dependency verification functionality for applications
//! that need to ensure required system dependencies are installed before execution.
//! It checks dependencies and provides clear error messages with installation guidance.
use thiserror::Error;
use tracing::{error, info};
use crate::{Dependency, DependencyManager, DetectionError};
// ============================================================================
// PUBLIC API - Main Functions
// ============================================================================
/// Verify that all required dependencies are installed
///
/// This function checks each dependency in the provided list and reports
/// clear errors if any are missing. It does NOT attempt automatic installation,
/// allowing the user to control when and how dependencies are installed.
///
/// # Errors
///
/// Returns an error if:
/// - One or more dependencies are not installed
/// - Detection system fails to check a dependency
///
/// # Example
///
/// ```no_run
/// use torrust_dependency_installer::{Dependency, verify_dependencies};
///
/// // Verify all dependencies for a full workflow
/// let deps = &[Dependency::OpenTofu, Dependency::Ansible, Dependency::Lxd];
/// verify_dependencies(deps)?;
///
/// // Verify only specific dependencies
/// let deps = &[Dependency::Ansible];
/// verify_dependencies(deps)?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn verify_dependencies(dependencies: &[Dependency]) -> Result<(), DependencyVerificationError> {
let manager = DependencyManager::new();
let mut missing = Vec::new();
info!("Verifying dependencies");
for &dep in dependencies {
let detector = manager.get_detector(dep);
match detector.is_installed() {
Ok(true) => {
info!(
dependency = detector.name(),
status = "installed",
"Dependency check passed"
);
}
Ok(false) => {
error!(
dependency = detector.name(),
status = "not installed",
"Dependency check failed"
);
missing.push(dep);
}
Err(e) => {
error!(
dependency = detector.name(),
error = %e,
"Failed to detect dependency"
);
return Err(DependencyVerificationError::DetectionFailed {
dependency: dep,
source: e,
});
}
}
}
if missing.is_empty() {
info!("All required dependencies are available");
Ok(())
} else {
Err(DependencyVerificationError::MissingDependencies {
dependencies: missing,
})
}
}
// ============================================================================
// ERROR TYPES - Secondary Concerns
// ============================================================================
/// Errors that can occur during dependency verification
#[derive(Debug, Error)]
pub enum DependencyVerificationError {
/// One or more required dependencies are not installed
#[error("Missing required dependencies: {}", format_dependency_list(.dependencies))]
MissingDependencies {
/// List of missing dependencies
dependencies: Vec<Dependency>,
},
/// Failed to detect if a dependency is installed
#[error("Failed to detect dependency '{dependency}': {source}")]
DetectionFailed {
/// The dependency that could not be detected
dependency: Dependency,
/// The underlying detection error
#[source]
source: DetectionError,
},
}
impl DependencyVerificationError {
/// Get actionable error message with installation instructions
#[must_use]
pub fn actionable_message(&self) -> String {
match self {
Self::MissingDependencies { dependencies } => {
let dep_list = format_dependency_list(dependencies);
format!(
"Missing required dependencies: {dep_list}\n\n\
To install all dependencies automatically, run:\n \
cargo run --bin dependency-installer install\n\n\
Or install specific dependencies:\n \
cargo run --bin dependency-installer install <dependency>\n\n\
For manual installation instructions, see:\n \
https://github.com/torrust/torrust-tracker-deployer/blob/main/packages/dependency-installer/README.md"
)
}
Self::DetectionFailed { dependency, source } => {
format!(
"Failed to detect dependency '{dependency}': {source}\n\n\
This may indicate a system configuration issue.\n\
Please ensure the dependency detection tool is working correctly."
)
}
}
}
}
// ============================================================================
// PRIVATE - Helper Functions
// ============================================================================
fn format_dependency_list(dependencies: &[Dependency]) -> String {
dependencies
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", ")
}
// NOTE: No unit tests here - verification logic is tested via Docker-based
// integration tests in packages/dependency-installer/tests/ which provide
// reliable, controlled environments. Unit tests would be environment-dependent
// and unreliable across different CI/dev setups.