mod runner;
use std::path::Path;
use crate::config::Config;
pub use runner::run_commands;
#[derive(Debug, Clone, serde::Serialize)]
pub struct CommandResult {
pub command: String,
pub passed: bool,
pub exit_code: Option<i32>,
pub output: String,
pub duration_ms: u64,
}
impl CommandResult {
#[must_use]
pub fn new(
command: String,
passed: bool,
exit_code: Option<i32>,
output: String,
duration_ms: u64,
) -> Self {
Self {
command,
passed,
exit_code,
output,
duration_ms,
}
}
#[must_use]
pub fn error_count(&self) -> usize {
self.output
.lines()
.filter(|line| {
let lower = line.to_lowercase();
lower.contains("error:") || lower.contains("error[") || lower.starts_with("error")
})
.count()
}
#[must_use]
pub fn warning_count(&self) -> usize {
self.output
.lines()
.filter(|line| {
let lower = line.to_lowercase();
lower.contains("warning:")
|| lower.contains("warn:")
|| lower.starts_with("warning")
})
.count()
}
}
#[derive(Debug, serde::Serialize)]
pub struct VerificationReport {
pub passed: bool,
pub output: String,
pub commands: Vec<CommandResult>,
pub duration_ms: u64,
}
impl VerificationReport {
#[must_use]
pub fn new(
passed: bool,
output: String,
commands: Vec<CommandResult>,
duration_ms: u64,
) -> Self {
Self {
passed,
output,
commands,
duration_ms,
}
}
#[must_use]
pub fn total_commands(&self) -> usize {
self.commands.len()
}
#[must_use]
pub fn passed_count(&self) -> usize {
self.commands.iter().filter(|c| c.passed).count()
}
#[must_use]
pub fn failed_count(&self) -> usize {
self.commands.iter().filter(|c| !c.passed).count()
}
#[must_use]
pub fn total_errors(&self) -> usize {
self.commands.iter().map(CommandResult::error_count).sum()
}
#[must_use]
pub fn total_warnings(&self) -> usize {
self.commands.iter().map(CommandResult::warning_count).sum()
}
}
pub fn run<F>(repo_root: &Path, on_command: F) -> VerificationReport
where
F: FnMut(&str, usize, usize),
{
let config = Config::load();
let commands = config.commands.get("check").cloned().unwrap_or_default();
runner::run_commands(repo_root, &commands, on_command)
}