1use crate::errors::{AnsibleError, Result};
7use std::process::Command;
8
9pub struct PlatformValidator;
68
69impl PlatformValidator {
70 pub fn check_platform() -> Result<()> {
72 #[cfg(not(any(
73 target_os = "linux",
74 target_os = "macos",
75 target_os = "freebsd",
76 target_os = "openbsd",
77 target_os = "netbsd"
78 )))]
79 {
80 return Err(AnsibleError::unsupported_platform(format!(
81 "ansible-rs only supports Unix-like systems. Current platform: {}",
82 std::env::consts::OS
83 )));
84 }
85
86 Ok(())
87 }
88
89 pub fn check_ansible_installation() -> Result<String> {
91 Self::check_platform()?;
92
93 let output = Command::new("ansible")
94 .arg("--version")
95 .output()
96 .map_err(|e| {
97 AnsibleError::command_not_found(format!(
98 "Ansible command not found. Please install Ansible first. Error: {}",
99 e
100 ))
101 })?;
102
103 if !output.status.success() {
104 let stderr = String::from_utf8_lossy(&output.stderr);
105 return Err(AnsibleError::command_failed(
106 "Failed to get Ansible version",
107 output.status.code(),
108 None,
109 Some(stderr.to_string()),
110 ));
111 }
112
113 let version_output = String::from_utf8_lossy(&output.stdout);
114 Ok(version_output.to_string())
115 }
116
117 pub fn check_ansible_playbook() -> Result<String> {
119 Self::check_platform()?;
120
121 let output = Command::new("ansible-playbook")
122 .arg("--version")
123 .output()
124 .map_err(|e| {
125 AnsibleError::command_not_found(format!(
126 "ansible-playbook command not found. Please install Ansible first. Error: {}",
127 e
128 ))
129 })?;
130
131 if !output.status.success() {
132 let stderr = String::from_utf8_lossy(&output.stderr);
133 return Err(AnsibleError::command_failed(
134 "Failed to get ansible-playbook version",
135 output.status.code(),
136 None,
137 Some(stderr.to_string()),
138 ));
139 }
140
141 let version_output = String::from_utf8_lossy(&output.stdout);
142 Ok(version_output.to_string())
143 }
144
145 pub fn check_ansible_vault() -> Result<String> {
147 Self::check_platform()?;
148
149 let output = Command::new("ansible-vault")
150 .arg("--help")
151 .output()
152 .map_err(|e| {
153 AnsibleError::command_not_found(format!(
154 "ansible-vault command not found. Please install Ansible first. Error: {}",
155 e
156 ))
157 })?;
158
159 if !output.status.success() {
160 let stderr = String::from_utf8_lossy(&output.stderr);
161 return Err(AnsibleError::command_failed(
162 "Failed to check ansible-vault availability",
163 output.status.code(),
164 None,
165 Some(stderr.to_string()),
166 ));
167 }
168
169 Ok("ansible-vault is available".to_string())
170 }
171
172 pub fn check_ansible_config() -> Result<String> {
174 Self::check_platform()?;
175
176 let output = Command::new("ansible-config")
177 .arg("--help")
178 .output()
179 .map_err(|e| {
180 AnsibleError::command_not_found(format!(
181 "ansible-config command not found. Please install Ansible first. Error: {}",
182 e
183 ))
184 })?;
185
186 if !output.status.success() {
187 let stderr = String::from_utf8_lossy(&output.stderr);
188 return Err(AnsibleError::command_failed(
189 "Failed to check ansible-config availability",
190 output.status.code(),
191 None,
192 Some(stderr.to_string()),
193 ));
194 }
195
196 Ok("ansible-config is available".to_string())
197 }
198
199 pub fn check_ansible_inventory() -> Result<String> {
201 Self::check_platform()?;
202
203 let output = Command::new("ansible-inventory")
204 .arg("--help")
205 .output()
206 .map_err(|e| {
207 AnsibleError::command_not_found(format!(
208 "ansible-inventory command not found. Please install Ansible first. Error: {}",
209 e
210 ))
211 })?;
212
213 if !output.status.success() {
214 let stderr = String::from_utf8_lossy(&output.stderr);
215 return Err(AnsibleError::command_failed(
216 "Failed to check ansible-inventory availability",
217 output.status.code(),
218 None,
219 Some(stderr.to_string()),
220 ));
221 }
222
223 Ok("ansible-inventory is available".to_string())
224 }
225
226 pub fn check_all_requirements() -> Result<SystemInfo> {
228 Self::check_platform()?;
229
230 let ansible_version = Self::check_ansible_installation()?;
231 let playbook_available = Self::check_ansible_playbook().is_ok();
232 let vault_available = Self::check_ansible_vault().is_ok();
233 let config_available = Self::check_ansible_config().is_ok();
234 let inventory_available = Self::check_ansible_inventory().is_ok();
235
236 Ok(SystemInfo {
237 platform: std::env::consts::OS.to_string(),
238 architecture: std::env::consts::ARCH.to_string(),
239 ansible_version,
240 playbook_available,
241 vault_available,
242 config_available,
243 inventory_available,
244 })
245 }
246
247 pub fn minimum_ansible_version() -> &'static str {
249 "2.9"
250 }
251
252 pub fn supported_platforms() -> Vec<&'static str> {
254 vec![
255 "linux",
256 "macos",
257 "freebsd",
258 "openbsd",
259 "netbsd",
260 ]
261 }
262
263 pub fn is_platform_supported() -> bool {
265 Self::supported_platforms().contains(&std::env::consts::OS)
266 }
267}
268
269#[derive(Debug, Clone)]
307pub struct SystemInfo {
308 pub platform: String,
310
311 pub architecture: String,
313
314 pub ansible_version: String,
316
317 pub playbook_available: bool,
319
320 pub vault_available: bool,
322
323 pub config_available: bool,
325
326 pub inventory_available: bool,
328}
329
330impl SystemInfo {
331 pub fn is_fully_supported(&self) -> bool {
333 self.playbook_available
334 && self.vault_available
335 && self.config_available
336 && self.inventory_available
337 }
338
339 pub fn feature_summary(&self) -> Vec<(String, bool)> {
341 vec![
342 ("ansible".to_string(), !self.ansible_version.is_empty()),
343 ("ansible-playbook".to_string(), self.playbook_available),
344 ("ansible-vault".to_string(), self.vault_available),
345 ("ansible-config".to_string(), self.config_available),
346 ("ansible-inventory".to_string(), self.inventory_available),
347 ]
348 }
349
350 pub fn missing_features(&self) -> Vec<String> {
352 let mut missing = Vec::new();
353
354 if self.ansible_version.is_empty() {
355 missing.push("ansible".to_string());
356 }
357 if !self.playbook_available {
358 missing.push("ansible-playbook".to_string());
359 }
360 if !self.vault_available {
361 missing.push("ansible-vault".to_string());
362 }
363 if !self.config_available {
364 missing.push("ansible-config".to_string());
365 }
366 if !self.inventory_available {
367 missing.push("ansible-inventory".to_string());
368 }
369
370 missing
371 }
372}
373
374impl std::fmt::Display for SystemInfo {
375 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376 writeln!(f, "System Information:")?;
377 writeln!(f, " Platform: {} ({})", self.platform, self.architecture)?;
378 writeln!(f, " Ansible Version: {}", self.ansible_version.trim())?;
379 writeln!(f, " Available Features:")?;
380
381 for (feature, available) in self.feature_summary() {
382 let status = if available { "✅" } else { "❌" };
383 writeln!(f, " {} {}", status, feature)?;
384 }
385
386 if !self.is_fully_supported() {
387 writeln!(f, " Missing Features: {:?}", self.missing_features())?;
388 }
389
390 Ok(())
391 }
392}
393
394pub fn validate_system() -> Result<()> {
396 PlatformValidator::check_platform()?;
397 PlatformValidator::check_ansible_installation()?;
398 Ok(())
399}
400
401pub fn get_system_info() -> Result<SystemInfo> {
403 PlatformValidator::check_all_requirements()
404}