Single-binary CLI that scaffolds testable Arduino projects, compiles, uploads, and monitors serial output. Templates embed a hardware abstraction layer, Google Mock infrastructure, and CMake-based host tests so application logic can be verified without hardware. Commands: new, doctor, setup, devices, build, upload, monitor 39 Rust tests (21 unit, 18 integration) Cross-platform: Linux and Windows
238 lines
5.6 KiB
Rust
238 lines
5.6 KiB
Rust
use anyhow::Result;
|
|
use colored::*;
|
|
|
|
use crate::board;
|
|
|
|
#[derive(Debug)]
|
|
pub struct SystemHealth {
|
|
pub arduino_cli_ok: bool,
|
|
pub arduino_cli_path: Option<String>,
|
|
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
|
|
}
|
|
}
|