use anyhow::Result; use colored::*; use crate::board; #[derive(Debug)] pub struct SystemHealth { pub arduino_cli_ok: bool, pub arduino_cli_path: Option, pub avr_core_ok: bool, pub avr_size_ok: bool, pub dialout_ok: bool, pub cmake_ok: bool, pub cpp_compiler_ok: bool, pub git_ok: bool, pub ports_found: usize, } impl SystemHealth { pub fn is_healthy(&self) -> bool { self.arduino_cli_ok && self.avr_core_ok } } pub fn run_diagnostics() -> Result<()> { println!( "{}", "Checking system health...".bright_yellow().bold() ); println!(); let health = check_system_health(); print_diagnostics(&health); println!(); if health.is_healthy() { println!( "{}", "System is ready for Arduino development." .bright_green() .bold() ); } else { println!( "{}", "Issues found. Run 'anvil setup' to fix." .bright_yellow() .bold() ); } println!(); Ok(()) } pub fn check_system_health() -> SystemHealth { // arduino-cli let (arduino_cli_ok, arduino_cli_path) = match board::find_arduino_cli() { Some(path) => (true, Some(path.display().to_string())), None => (false, None), }; // AVR core let avr_core_ok = if let Some(ref path_str) = arduino_cli_path { let path = std::path::Path::new(path_str); board::is_avr_core_installed(path) } else { false }; // avr-size (optional) let avr_size_ok = which::which("avr-size").is_ok(); // dialout group (Linux only) let dialout_ok = check_dialout(); // cmake (optional -- for host tests) let cmake_ok = which::which("cmake").is_ok(); // C++ compiler (optional -- for host tests) let cpp_compiler_ok = which::which("g++").is_ok() || which::which("clang++").is_ok(); // git let git_ok = which::which("git").is_ok(); // Serial ports let ports_found = board::list_ports().len(); SystemHealth { arduino_cli_ok, arduino_cli_path, avr_core_ok, avr_size_ok, dialout_ok, cmake_ok, cpp_compiler_ok, git_ok, ports_found, } } fn print_diagnostics(health: &SystemHealth) { println!("{}", "Required:".bright_yellow().bold()); println!(); // arduino-cli if health.arduino_cli_ok { println!( " {} arduino-cli {}", "ok".green(), health .arduino_cli_path .as_ref() .unwrap_or(&String::new()) .bright_black() ); } else { println!(" {} arduino-cli {}", "MISSING".red(), "not found in PATH".red()); } // AVR core if health.avr_core_ok { println!(" {} arduino:avr core installed", "ok".green()); } else if health.arduino_cli_ok { println!( " {} arduino:avr core {}", "MISSING".red(), "run: anvil setup".red() ); } else { println!( " {} arduino:avr core {}", "MISSING".red(), "(needs arduino-cli first)".bright_black() ); } println!(); println!("{}", "Optional:".bright_yellow().bold()); println!(); // avr-size if health.avr_size_ok { println!(" {} avr-size (binary size reporting)", "ok".green()); } else { println!( " {} avr-size {}", "--".bright_black(), "install: sudo apt install gcc-avr".bright_black() ); } // dialout #[cfg(unix)] { if health.dialout_ok { println!(" {} user in dialout group", "ok".green()); } else { println!( " {} dialout group {}", "WARN".yellow(), "run: sudo usermod -aG dialout $USER".yellow() ); } } // cmake if health.cmake_ok { println!(" {} cmake (for host-side tests)", "ok".green()); } else { println!( " {} cmake {}", "--".bright_black(), "install: sudo apt install cmake".bright_black() ); } // C++ compiler if health.cpp_compiler_ok { println!(" {} C++ compiler (g++/clang++)", "ok".green()); } else { println!( " {} C++ compiler {}", "--".bright_black(), "install: sudo apt install g++".bright_black() ); } // git if health.git_ok { println!(" {} git", "ok".green()); } else { println!( " {} git {}", "--".bright_black(), "install: sudo apt install git".bright_black() ); } println!(); println!("{}", "Hardware:".bright_yellow().bold()); println!(); if health.ports_found > 0 { println!( " {} {} serial port(s) detected", "ok".green(), health.ports_found ); } else { println!( " {} no serial ports {}", "--".bright_black(), "(plug in a board to detect)".bright_black() ); } } fn check_dialout() -> bool { #[cfg(unix)] { let output = std::process::Command::new("groups") .output(); match output { Ok(out) => { let groups = String::from_utf8_lossy(&out.stdout); groups.contains("dialout") } Err(_) => false, } } #[cfg(not(unix))] { true // Not applicable on Windows } }