use alembic_adapter_test::{load_cases, run_builtin, run_cases, Failure, Outcome};
use clap::Parser;
use std::path::PathBuf;
use std::process::ExitCode;
use std::time::Duration;
#[derive(Parser)]
#[command(about = "check an external adapter executable for protocol conformance")]
struct Cli {
#[arg(long)]
cases: Option<PathBuf>,
#[arg(long, default_value_t = 10)]
timeout: u64,
#[arg(last = true, required = true)]
adapter: Vec<String>,
}
fn main() -> ExitCode {
let cli = Cli::parse();
let timeout = Duration::from_secs(cli.timeout);
let mut outcomes = run_builtin(&cli.adapter, timeout);
if let Some(path) = cli.cases {
match load_cases(&path) {
Ok(cases) => outcomes.extend(run_cases(&cli.adapter, timeout, &cases)),
Err(e) => {
eprintln!("error: {e}");
return ExitCode::from(2);
}
}
}
report(&outcomes)
}
fn report(outcomes: &[Outcome]) -> ExitCode {
let width = outcomes.iter().map(|o| o.name.len()).max().unwrap_or(0);
let mut failed = 0;
for outcome in outcomes {
let name = &outcome.name;
match &outcome.failure {
None => println!("{name:<width$} ok"),
Some(failure) => {
failed += 1;
println!("{name:<width$} FAILED");
print_failure(failure);
}
}
}
let passed = outcomes.len() - failed;
println!();
if failed == 0 {
println!("{passed} passed");
ExitCode::SUCCESS
} else {
println!("{passed} passed, {failed} failed");
ExitCode::from(1)
}
}
fn print_failure(failure: &Failure) {
println!(" error: {}", failure.message);
println!(" status: {}", failure.status);
print_stream("stdout", &failure.stdout);
print_stream("stderr", &failure.stderr);
}
fn print_stream(label: &str, content: &str) {
let content = content.trim_end_matches('\n');
if content.is_empty() {
println!(" {label}: <empty>");
} else {
println!(" {label}:");
for line in content.lines() {
println!(" {line}");
}
}
}