use crate::args::{Cli, Command, Format};
use std::fmt::Write as _;
use std::path::Path;
use wickra_benchmark_core::{load_candles, run_case, run_suite, BenchmarkCase, CaseResult, Suite};
pub struct Output {
pub text: String,
pub code: u8,
}
pub fn run(cli: &Cli) -> Result<Output, String> {
match &cli.command {
Command::RunCase {
case,
data_root,
format,
} => run_case_cmd(case, data_root, *format),
Command::RunSuite {
suite,
data_root,
format,
} => run_suite_cmd(suite, data_root, *format),
Command::ListCases { suite } => list_cases_cmd(suite),
}
}
fn load_case(path: &Path) -> Result<BenchmarkCase, String> {
let text = std::fs::read_to_string(path).map_err(|e| format!("{}: {e}", path.display()))?;
let parsed = if is_toml(path) {
BenchmarkCase::from_toml(&text)
} else {
BenchmarkCase::from_json(&text)
};
parsed.map_err(|e| format!("{}: {e}", path.display()))
}
fn load_suite(path: &Path) -> Result<Suite, String> {
let text = std::fs::read_to_string(path).map_err(|e| format!("{}: {e}", path.display()))?;
let parsed = if is_toml(path) {
Suite::from_toml(&text)
} else {
Suite::from_json(&text)
};
parsed.map_err(|e| format!("{}: {e}", path.display()))
}
fn is_toml(path: &Path) -> bool {
path.extension()
.is_some_and(|e| e.eq_ignore_ascii_case("toml"))
}
fn run_case_cmd(case_path: &Path, data_root: &Path, format: Format) -> Result<Output, String> {
let case = load_case(case_path)?;
let candles = load_candles(&data_root.join(&case.dataset_ref)).map_err(|e| e.to_string())?;
let result = run_case(&case, &candles).map_err(|e| e.to_string())?;
let passed = result.passed && result.hash_match;
let text = match format {
Format::Json => serde_json::to_string(&result).map_err(|e| e.to_string())?,
Format::Text => {
let mut out = render_table(std::slice::from_ref(&result));
let _ = writeln!(out, "{}/1 passed", usize::from(passed));
out
}
};
Ok(Output {
text,
code: u8::from(!passed),
})
}
fn run_suite_cmd(suite_path: &Path, data_root: &Path, format: Format) -> Result<Output, String> {
let suite = load_suite(suite_path)?;
let report = run_suite(&suite, data_root).map_err(|e| e.to_string())?;
let text = match format {
Format::Json => serde_json::to_string(&report).map_err(|e| e.to_string())?,
Format::Text => {
let mut out = render_table(&report.results);
let _ = writeln!(out, "{}/{} passed", report.passed, report.results.len());
out
}
};
Ok(Output {
text,
code: u8::from(report.failed > 0),
})
}
fn list_cases_cmd(suite_path: &Path) -> Result<Output, String> {
let suite = load_suite(suite_path)?;
let mut text = String::new();
for id in suite.case_ids() {
let _ = writeln!(text, "{id}");
}
Ok(Output { text, code: 0 })
}
fn render_table(results: &[CaseResult]) -> String {
let id_width = results
.iter()
.map(|r| r.id.len())
.chain(std::iter::once("id".len()))
.max()
.unwrap_or(2);
let mut out = String::new();
let _ = writeln!(
out,
"{:<id_width$} {:<6} {:<10} hash",
"id", "passed", "hash_match"
);
for r in results {
let _ = writeln!(
out,
"{:<id_width$} {:<6} {:<10} {}",
r.id,
r.passed,
r.hash_match,
&r.hash[..r.hash.len().min(12)]
);
}
out
}