Skip to main content

cargo_mate/tools/
env_check.rs

1use super::{Tool, Result, ToolError, common_options, parse_output_format, OutputFormat};
2use clap::{Arg, ArgMatches, Command};
3use colored::*;
4use std::path::Path;
5use std::fs;
6use std::env;
7use std::process::Command as ProcessCommand;
8use std::collections::HashMap;
9use serde::{Deserialize, Serialize};
10#[derive(Debug, Clone)]
11pub struct EnvCheckTool;
12#[derive(Debug, Clone, Serialize, Deserialize)]
13struct EnvironmentReport {
14    overall_status: String,
15    checks_passed: usize,
16    checks_failed: usize,
17    checks_warning: usize,
18    rust_toolchain: RustToolchainInfo,
19    system_info: SystemInfo,
20    dependencies: DependencyStatus,
21    configuration: ConfigStatus,
22    recommendations: Vec<String>,
23    issues: Vec<EnvironmentIssue>,
24    timestamp: String,
25}
26#[derive(Debug, Clone, Serialize, Deserialize)]
27struct RustToolchainInfo {
28    version: String,
29    channel: String,
30    target: String,
31    components: Vec<String>,
32    is_compatible: bool,
33    issues: Vec<String>,
34}
35#[derive(Debug, Clone, Serialize, Deserialize)]
36struct SystemInfo {
37    os: String,
38    arch: String,
39    memory_gb: u64,
40    available_disk_gb: u64,
41    cpu_cores: usize,
42}
43#[derive(Debug, Clone, Serialize, Deserialize)]
44struct DependencyStatus {
45    required_tools: HashMap<String, ToolStatus>,
46    optional_tools: HashMap<String, ToolStatus>,
47    rust_components: HashMap<String, ComponentStatus>,
48}
49#[derive(Debug, Clone, Serialize, Deserialize)]
50struct ConfigStatus {
51    cargo_config_exists: bool,
52    rustfmt_config_exists: bool,
53    clippy_config_exists: bool,
54    environment_variables: HashMap<String, EnvVarStatus>,
55}
56#[derive(Debug, Clone, Serialize, Deserialize)]
57struct ToolStatus {
58    installed: bool,
59    version: Option<String>,
60    required_version: Option<String>,
61    status: String,
62    path: Option<String>,
63}
64#[derive(Debug, Clone, Serialize, Deserialize)]
65struct ComponentStatus {
66    installed: bool,
67    status: String,
68}
69#[derive(Debug, Clone, Serialize, Deserialize)]
70struct EnvVarStatus {
71    set: bool,
72    value: Option<String>,
73    required: bool,
74    masked: bool,
75}
76#[derive(Debug, Clone, Serialize, Deserialize)]
77struct EnvironmentIssue {
78    severity: String,
79    category: String,
80    message: String,
81    solution: String,
82    command: Option<String>,
83}
84impl EnvCheckTool {
85    pub fn new() -> Self {
86        Self
87    }
88    fn check_rust_toolchain(&self) -> Result<RustToolchainInfo> {
89        let mut issues = Vec::new();
90        let version_output = ProcessCommand::new("rustc")
91            .arg("--version")
92            .output()
93            .map_err(|_| ToolError::ExecutionFailed("rustc not found".to_string()))?;
94        let version_str = String::from_utf8_lossy(&version_output.stdout);
95        let version = version_str
96            .split_whitespace()
97            .nth(1)
98            .unwrap_or("unknown")
99            .to_string();
100        let is_compatible = self.is_rust_version_compatible(&version);
101        if !is_compatible {
102            issues.push(format!("Rust version {} may be outdated", version));
103        }
104        let channel = if version.contains("nightly") {
105            "nightly".to_string()
106        } else if version.contains("beta") {
107            "beta".to_string()
108        } else {
109            "stable".to_string()
110        };
111        let target_output = ProcessCommand::new("rustc")
112            .args(&["--print", "target-list"])
113            .output()
114            .ok();
115        let target = target_output
116            .and_then(|output| {
117                String::from_utf8_lossy(&output.stdout)
118                    .lines()
119                    .next()
120                    .map(|s| s.to_string())
121            })
122            .unwrap_or_else(|| "unknown".to_string());
123        let components = self.check_rust_components()?;
124        Ok(RustToolchainInfo {
125            version,
126            channel,
127            target,
128            components,
129            is_compatible,
130            issues,
131        })
132    }
133    fn is_rust_version_compatible(&self, version: &str) -> bool {
134        if let Some(version_part) = version.split('+').next() {
135            let parts: Vec<&str> = version_part.split('.').collect();
136            if parts.len() >= 2 {
137                if let (Ok(major), Ok(minor)) = (
138                    parts[0].parse::<u32>(),
139                    parts[1].parse::<u32>(),
140                ) {
141                    return major > 1 || (major == 1 && minor >= 70);
142                }
143            }
144        }
145        false
146    }
147    fn check_rust_components(&self) -> Result<Vec<String>> {
148        let output = ProcessCommand::new("rustup")
149            .args(&["component", "list", "--installed"])
150            .output()
151            .map_err(|_| ToolError::ExecutionFailed("rustup not found".to_string()))?;
152        let components = String::from_utf8_lossy(&output.stdout)
153            .lines()
154            .map(|line| line.trim().to_string())
155            .filter(|line| !line.is_empty())
156            .collect();
157        Ok(components)
158    }
159    fn check_system_info(&self) -> Result<SystemInfo> {
160        let os = env::consts::OS.to_string();
161        let arch = env::consts::ARCH.to_string();
162        let memory_gb = self.get_memory_info();
163        let available_disk_gb = self.get_disk_info();
164        let cpu_cores = self.get_cpu_cores_fallback();
165        Ok(SystemInfo {
166            os,
167            arch,
168            memory_gb,
169            available_disk_gb,
170            cpu_cores,
171        })
172    }
173    fn get_memory_info(&self) -> u64 {
174        if cfg!(target_os = "linux") {
175            if let Ok(contents) = fs::read_to_string("/proc/meminfo") {
176                for line in contents.lines() {
177                    if line.starts_with("MemTotal:") {
178                        if let Some(kb_str) = line.split_whitespace().nth(1) {
179                            if let Ok(kb) = kb_str.parse::<u64>() {
180                                return kb / 1024 / 1024;
181                            }
182                        }
183                    }
184                }
185            }
186        }
187        0
188    }
189    fn get_disk_info(&self) -> u64 {
190        if let Ok(stat) = fs::metadata(".") { 100 } else { 0 }
191    }
192    fn get_cpu_cores_fallback(&self) -> usize {
193        if cfg!(target_os = "linux") {
194            if let Ok(contents) = fs::read_to_string("/proc/cpuinfo") {
195                let processor_count = contents
196                    .lines()
197                    .filter(|line| line.starts_with("processor"))
198                    .count();
199                if processor_count > 0 {
200                    return processor_count;
201                }
202            }
203        } else if cfg!(target_os = "macos") {
204            if let Ok(output) = ProcessCommand::new("sysctl")
205                .args(&["-n", "hw.ncpu"])
206                .output()
207            {
208                if let Ok(core_str) = String::from_utf8(output.stdout) {
209                    if let Ok(cores) = core_str.trim().parse::<usize>() {
210                        return cores;
211                    }
212                }
213            }
214        }
215        4
216    }
217    fn check_dependencies(&self) -> Result<DependencyStatus> {
218        let mut required_tools = HashMap::new();
219        let mut optional_tools = HashMap::new();
220        let mut rust_components = HashMap::new();
221        let required = vec!["cargo", "rustc", "rustup", "git"];
222        for tool in required {
223            required_tools.insert(tool.to_string(), self.check_tool(tool)?);
224        }
225        let optional = vec!["docker", "node", "npm", "yarn", "python", "python3"];
226        for tool in optional {
227            optional_tools.insert(tool.to_string(), self.check_tool(tool)?);
228        }
229        let components = vec!["rustfmt", "clippy", "rust-docs", "rust-analyzer"];
230        for component in components {
231            rust_components
232                .insert(component.to_string(), self.check_rust_component(component)?);
233        }
234        Ok(DependencyStatus {
235            required_tools,
236            optional_tools,
237            rust_components,
238        })
239    }
240    fn check_tool(&self, tool: &str) -> Result<ToolStatus> {
241        let output = ProcessCommand::new(tool).arg("--version").output();
242        match output {
243            Ok(result) => {
244                if result.status.success() {
245                    let version = String::from_utf8_lossy(&result.stdout)
246                        .lines()
247                        .next()
248                        .unwrap_or("unknown")
249                        .to_string();
250                    let path_output = ProcessCommand::new("which")
251                        .arg(tool)
252                        .output()
253                        .ok();
254                    let path = path_output
255                        .and_then(|p| {
256                            if p.status.success() {
257                                String::from_utf8_lossy(&p.stdout).trim().to_string().into()
258                            } else {
259                                None
260                            }
261                        });
262                    Ok(ToolStatus {
263                        installed: true,
264                        version: Some(version),
265                        required_version: None,
266                        status: "ok".to_string(),
267                        path,
268                    })
269                } else {
270                    Ok(ToolStatus {
271                        installed: false,
272                        version: None,
273                        required_version: None,
274                        status: "not_found".to_string(),
275                        path: None,
276                    })
277                }
278            }
279            Err(_) => {
280                Ok(ToolStatus {
281                    installed: false,
282                    version: None,
283                    required_version: None,
284                    status: "not_found".to_string(),
285                    path: None,
286                })
287            }
288        }
289    }
290    fn check_rust_component(&self, component: &str) -> Result<ComponentStatus> {
291        let output = ProcessCommand::new("rustup")
292            .args(&["component", "list", "--installed"])
293            .output()
294            .map_err(|_| ToolError::ExecutionFailed("rustup not found".to_string()))?;
295        let installed_components = String::from_utf8_lossy(&output.stdout);
296        let installed = installed_components.contains(component);
297        Ok(ComponentStatus {
298            installed,
299            status: if installed { "ok".to_string() } else { "missing".to_string() },
300        })
301    }
302    fn check_configuration(&self) -> Result<ConfigStatus> {
303        let cargo_config_exists = Path::new("Cargo.toml").exists();
304        let rustfmt_config_exists = Path::new(".rustfmt.toml").exists()
305            || Path::new("rustfmt.toml").exists();
306        let clippy_config_exists = Path::new(".clippy.toml").exists()
307            || Path::new("clippy.toml").exists();
308        let env_vars = vec!["RUST_BACKTRACE", "CARGO_HOME", "RUSTUP_HOME", "PATH"];
309        let mut environment_variables = HashMap::new();
310        for var in env_vars {
311            let value = env::var(var).ok();
312            let set = value.is_some();
313            let required = matches!(var, "PATH");
314            let masked = matches!(var, "RUST_BACKTRACE" | "CARGO_HOME" | "RUSTUP_HOME");
315            environment_variables
316                .insert(
317                    var.to_string(),
318                    EnvVarStatus {
319                        set,
320                        value: if masked {
321                            Some("***masked***".to_string())
322                        } else {
323                            value
324                        },
325                        required,
326                        masked,
327                    },
328                );
329        }
330        Ok(ConfigStatus {
331            cargo_config_exists,
332            rustfmt_config_exists,
333            clippy_config_exists,
334            environment_variables,
335        })
336    }
337    fn analyze_issues(&self, report: &EnvironmentReport) -> Vec<EnvironmentIssue> {
338        let mut issues = Vec::new();
339        if !report.rust_toolchain.is_compatible {
340            issues
341                .push(EnvironmentIssue {
342                    severity: "warning".to_string(),
343                    category: "rust_toolchain".to_string(),
344                    message: format!(
345                        "Rust version {} may be outdated", report.rust_toolchain.version
346                    ),
347                    solution: "Update Rust to version 1.70.0 or later".to_string(),
348                    command: Some("rustup update stable".to_string()),
349                });
350        }
351        for (tool, status) in &report.dependencies.required_tools {
352            if !status.installed {
353                issues
354                    .push(EnvironmentIssue {
355                        severity: "error".to_string(),
356                        category: "dependencies".to_string(),
357                        message: format!("Required tool '{}' is not installed", tool),
358                        solution: format!(
359                            "Install {} using your system package manager", tool
360                        ),
361                        command: None,
362                    });
363            }
364        }
365        for (component, status) in &report.dependencies.rust_components {
366            if !status.installed {
367                issues
368                    .push(EnvironmentIssue {
369                        severity: "warning".to_string(),
370                        category: "rust_components".to_string(),
371                        message: format!(
372                            "Rust component '{}' is not installed", component
373                        ),
374                        solution: format!(
375                            "Install with: rustup component add {}", component
376                        ),
377                        command: Some(format!("rustup component add {}", component)),
378                    });
379            }
380        }
381        if report.system_info.memory_gb < 4 {
382            issues
383                .push(EnvironmentIssue {
384                    severity: "warning".to_string(),
385                    category: "system".to_string(),
386                    message: format!(
387                        "Low memory: {}GB available", report.system_info.memory_gb
388                    ),
389                    solution: "Consider upgrading to at least 8GB RAM for better Rust compilation performance"
390                        .to_string(),
391                    command: None,
392                });
393        }
394        issues
395    }
396    fn generate_recommendations(&self, report: &EnvironmentReport) -> Vec<String> {
397        let mut recommendations = Vec::new();
398        recommendations
399            .push("šŸŽÆ Keep Rust updated to the latest stable version".to_string());
400        recommendations
401            .push(
402                "šŸ“š Install rustfmt and clippy for code formatting and linting"
403                    .to_string(),
404            );
405        recommendations
406            .push(
407                "šŸ”§ Set RUST_BACKTRACE=1 for better error messages during development"
408                    .to_string(),
409            );
410        if !report
411            .dependencies
412            .optional_tools
413            .get("docker")
414            .map_or(false, |t| t.installed)
415        {
416            recommendations
417                .push(
418                    "🐳 Consider installing Docker for integration testing".to_string(),
419                );
420        }
421        if !report.configuration.rustfmt_config_exists {
422            recommendations
423                .push(
424                    "šŸ“ Create .rustfmt.toml for consistent code formatting"
425                        .to_string(),
426                );
427        }
428        if !report.configuration.clippy_config_exists {
429            recommendations
430                .push("šŸ” Create .clippy.toml to configure linting rules".to_string());
431        }
432        recommendations
433    }
434    fn determine_overall_status(&self, report: &EnvironmentReport) -> String {
435        let critical_issues = report
436            .issues
437            .iter()
438            .filter(|i| i.severity == "error")
439            .count();
440        let warning_issues = report
441            .issues
442            .iter()
443            .filter(|i| i.severity == "warning")
444            .count();
445        if critical_issues > 0 {
446            "āŒ Issues Found".to_string()
447        } else if warning_issues > 0 {
448            "āš ļø  Needs Attention".to_string()
449        } else {
450            "āœ… All Good".to_string()
451        }
452    }
453}
454impl Tool for EnvCheckTool {
455    fn name(&self) -> &'static str {
456        "env-check"
457    }
458    fn description(&self) -> &'static str {
459        "Validate development environment and provide setup recommendations"
460    }
461    fn command(&self) -> Command {
462        Command::new(self.name())
463            .about(self.description())
464            .long_about(
465                "Comprehensive development environment validation for Rust projects. Checks Rust toolchain, system resources, required tools, and provides actionable recommendations for optimizing your development setup.",
466            )
467            .args(
468                &[
469                    Arg::new("detailed")
470                        .long("detailed")
471                        .short('d')
472                        .help("Show detailed system information")
473                        .action(clap::ArgAction::SetTrue),
474                    Arg::new("fix")
475                        .long("fix")
476                        .help("Attempt to automatically fix issues")
477                        .action(clap::ArgAction::SetTrue),
478                    Arg::new("export")
479                        .long("export")
480                        .help("Export environment report to file")
481                        .value_name("FILE"),
482                    Arg::new("check-only")
483                        .long("check-only")
484                        .help("Only run checks, don't provide recommendations")
485                        .action(clap::ArgAction::SetTrue),
486                ],
487            )
488            .args(&common_options())
489    }
490    fn execute(&self, matches: &ArgMatches) -> Result<()> {
491        let detailed = matches.get_flag("detailed");
492        let fix = matches.get_flag("fix");
493        let export_file = matches.get_one::<String>("export");
494        let check_only = matches.get_flag("check-only");
495        let dry_run = matches.get_flag("dry-run");
496        let verbose = matches.get_flag("verbose");
497        let output_format = parse_output_format(matches);
498        println!(
499            "šŸ” {} - {}", "CargoMate EnvCheck".bold().blue(), self.description().cyan()
500        );
501        if verbose {
502            println!("   šŸ“Š Analyzing development environment...");
503        }
504        let rust_toolchain = self.check_rust_toolchain()?;
505        let system_info = self.check_system_info()?;
506        let dependencies = self.check_dependencies()?;
507        let configuration = self.check_configuration()?;
508        let mut report = EnvironmentReport {
509            overall_status: String::new(),
510            checks_passed: 0,
511            checks_failed: 0,
512            checks_warning: 0,
513            rust_toolchain,
514            system_info,
515            dependencies,
516            configuration,
517            recommendations: Vec::new(),
518            issues: Vec::new(),
519            timestamp: chrono::Utc::now().to_rfc3339(),
520        };
521        report.issues = self.analyze_issues(&report);
522        report.recommendations = if check_only {
523            Vec::new()
524        } else {
525            self.generate_recommendations(&report)
526        };
527        report.checks_passed = report
528            .dependencies
529            .required_tools
530            .values()
531            .filter(|t| t.installed)
532            .count()
533            + report.dependencies.optional_tools.values().filter(|t| t.installed).count()
534            + report
535                .dependencies
536                .rust_components
537                .values()
538                .filter(|c| c.installed)
539                .count();
540        report.checks_failed = report
541            .issues
542            .iter()
543            .filter(|i| i.severity == "error")
544            .count();
545        report.checks_warning = report
546            .issues
547            .iter()
548            .filter(|i| i.severity == "warning")
549            .count();
550        report.overall_status = self.determine_overall_status(&report);
551        if fix && !dry_run {
552            self.apply_fixes(&report.issues)?;
553        }
554        if let Some(file_path) = export_file {
555            if !dry_run {
556                let json_report = serde_json::to_string_pretty(&report)?;
557                fs::write(file_path, json_report)?;
558                println!("  šŸ’¾ Report exported to: {}", file_path.cyan());
559            }
560        }
561        match output_format {
562            OutputFormat::Human => {
563                self.display_human_report(&report, detailed, check_only);
564            }
565            OutputFormat::Json => {
566                let json_report = serde_json::to_string_pretty(&report)?;
567                println!("{}", json_report);
568            }
569            OutputFormat::Table => {
570                self.display_table_report(&report);
571            }
572        }
573        if report.checks_failed > 0 {
574            println!(
575                "\nāŒ Environment check failed - {} critical issues found", report
576                .checks_failed
577            );
578            std::process::exit(1);
579        } else if report.checks_warning > 0 {
580            println!(
581                "\nāš ļø  Environment check completed with {} warnings", report
582                .checks_warning
583            );
584        } else {
585            println!("\nāœ… Environment check passed - all systems go!");
586        }
587        Ok(())
588    }
589}
590impl EnvCheckTool {
591    fn display_human_report(
592        &self,
593        report: &EnvironmentReport,
594        detailed: bool,
595        check_only: bool,
596    ) {
597        println!("\nšŸ“Š {}", "Environment Report".bold().underline());
598        println!("Status: {}", report.overall_status);
599        println!("Timestamp: {}", report.timestamp);
600        println!(
601            "Checks: āœ… {} passed, āŒ {} failed, āš ļø  {} warnings", report
602            .checks_passed, report.checks_failed, report.checks_warning
603        );
604        println!("\nšŸ¦€ {}", "Rust Toolchain".bold());
605        println!("   Version: {}", report.rust_toolchain.version);
606        println!("   Channel: {}", report.rust_toolchain.channel);
607        println!("   Target: {}", report.rust_toolchain.target);
608        println!(
609            "   Compatible: {}", if report.rust_toolchain.is_compatible { "āœ… Yes" }
610            else { "āŒ No" }
611        );
612        if detailed {
613            println!("   Components: {}", report.rust_toolchain.components.join(", "));
614        }
615        if !report.rust_toolchain.issues.is_empty() {
616            for issue in &report.rust_toolchain.issues {
617                println!("   āš ļø  {}", issue.yellow());
618            }
619        }
620        if detailed {
621            println!("\nšŸ’» {}", "System Information".bold());
622            println!("   OS: {} {}", report.system_info.os, report.system_info.arch);
623            println!("   Memory: {} GB", report.system_info.memory_gb);
624            println!(
625                "   Disk Space: {} GB available", report.system_info.available_disk_gb
626            );
627            println!("   CPU Cores: {}", report.system_info.cpu_cores);
628        }
629        println!("\nšŸ“¦ {}", "Dependencies".bold());
630        println!("   {}", "Required Tools:".underline());
631        for (tool, status) in &report.dependencies.required_tools {
632            let status_icon = if status.installed { "āœ…" } else { "āŒ" };
633            let version = status
634                .version
635                .as_ref()
636                .map(|v| format!(" ({})", v))
637                .unwrap_or_default();
638            println!("     {} {} {}", status_icon, tool, version);
639        }
640        if detailed {
641            println!("   {}", "Optional Tools:".underline());
642            for (tool, status) in &report.dependencies.optional_tools {
643                let status_icon = if status.installed { "āœ…" } else { "⚪" };
644                let version = status
645                    .version
646                    .as_ref()
647                    .map(|v| format!(" ({})", v))
648                    .unwrap_or_default();
649                println!("     {} {} {}", status_icon, tool, version);
650            }
651            println!("   {}", "Rust Components:".underline());
652            for (component, status) in &report.dependencies.rust_components {
653                let status_icon = if status.installed { "āœ…" } else { "āŒ" };
654                println!("     {} {}", status_icon, component);
655            }
656        }
657        println!("\nāš™ļø  {}", "Configuration".bold());
658        println!(
659            "   Cargo.toml: {}", if report.configuration.cargo_config_exists {
660            "āœ… Found" } else { "āŒ Missing" }
661        );
662        println!(
663            "   Rustfmt config: {}", if report.configuration.rustfmt_config_exists {
664            "āœ… Found" } else { "āŒ Missing" }
665        );
666        println!(
667            "   Clippy config: {}", if report.configuration.clippy_config_exists {
668            "āœ… Found" } else { "āŒ Missing" }
669        );
670        if detailed {
671            println!("   {}", "Environment Variables:".underline());
672            for (var, status) in &report.configuration.environment_variables {
673                let status_icon = if status.set { "āœ…" } else { "āŒ" };
674                let default_value = "not set".to_string();
675                let value = status.value.as_ref().unwrap_or(&default_value);
676                let required = if status.required { " (required)" } else { "" };
677                println!("     {} {} = {}{}", status_icon, var, value, required);
678            }
679        }
680        if !report.issues.is_empty() {
681            println!("\n🚨 {}", "Issues Found".bold());
682            for issue in &report.issues {
683                let severity_icon = match issue.severity.as_str() {
684                    "error" => "āŒ",
685                    "warning" => "āš ļø ",
686                    _ => "ā„¹ļø ",
687                };
688                println!("   {} {}", severity_icon, issue.message);
689                println!("      šŸ’” Solution: {}", issue.solution);
690                if let Some(cmd) = &issue.command {
691                    println!("      šŸ› ļø  Command: {}", cmd.cyan());
692                }
693                println!();
694            }
695        }
696        if !check_only && !report.recommendations.is_empty() {
697            println!("\nšŸ’” {}", "Recommendations".bold());
698            for recommendation in &report.recommendations {
699                println!("   • {}", recommendation);
700            }
701        }
702    }
703    fn display_table_report(&self, report: &EnvironmentReport) {
704        println!(
705            "{:<25} {:<12} {:<12} {:<12}", "Category", "Status", "Passed", "Issues"
706        );
707        println!("{}", "─".repeat(70));
708        let rust_status = if report.rust_toolchain.is_compatible {
709            "āœ… OK"
710        } else {
711            "āš ļø  WARN"
712        };
713        println!(
714            "{:<25} {:<12} {:<12} {:<12}", "Rust Toolchain", rust_status, "1/1", "0"
715        );
716        let deps_passed = report
717            .dependencies
718            .required_tools
719            .values()
720            .filter(|t| t.installed)
721            .count();
722        let deps_total = report.dependencies.required_tools.len();
723        let deps_status = if deps_passed == deps_total { "āœ… OK" } else { "āŒ FAIL" };
724        println!(
725            "{:<25} {:<12} {:<12} {:<12}", "Required Tools", deps_status,
726            format!("{}/{}", deps_passed, deps_total), "0"
727        );
728        let comp_passed = report
729            .dependencies
730            .rust_components
731            .values()
732            .filter(|c| c.installed)
733            .count();
734        let comp_total = report.dependencies.rust_components.len();
735        let comp_status = if comp_passed == comp_total {
736            "āœ… OK"
737        } else {
738            "āš ļø  WARN"
739        };
740        println!(
741            "{:<25} {:<12} {:<12} {:<12}", "Rust Components", comp_status,
742            format!("{}/{}", comp_passed, comp_total), "0"
743        );
744    }
745    fn apply_fixes(&self, issues: &[EnvironmentIssue]) -> Result<()> {
746        println!("\nšŸ”§ {}", "Applying Automatic Fixes".bold());
747        for issue in issues {
748            if let Some(command) = &issue.command {
749                println!("   šŸ› ļø  Running: {}", command.cyan());
750                let output = ProcessCommand::new("sh").arg("-c").arg(command).output();
751                match output {
752                    Ok(result) => {
753                        if result.status.success() {
754                            println!("      āœ… Success");
755                        } else {
756                            println!(
757                                "      āŒ Failed: {}", String::from_utf8_lossy(& result
758                                .stderr)
759                            );
760                        }
761                    }
762                    Err(e) => {
763                        println!("      āŒ Error: {}", e);
764                    }
765                }
766            }
767        }
768        Ok(())
769    }
770}
771impl Default for EnvCheckTool {
772    fn default() -> Self {
773        Self::new()
774    }
775}