use std::path::Path;
use std::process::Command;
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VerifyStep {
Format,
Clippy,
Test,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StepResult {
Pass,
Fail(String),
Skip(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VerifyReport {
pub format: StepResult,
pub clippy: StepResult,
pub test: StepResult,
}
#[derive(Error, Debug)]
pub enum VerifyError {
#[error("Failed to execute cargo command: {0}")]
CommandExecution(String),
#[error("I/O error during verification: {0}")]
Io(#[from] std::io::Error),
}
impl VerifyReport {
pub fn is_success(&self) -> bool {
matches!(self.format, StepResult::Pass | StepResult::Skip(_))
&& matches!(self.clippy, StepResult::Pass | StepResult::Skip(_))
&& matches!(self.test, StepResult::Pass | StepResult::Skip(_))
}
}
pub fn run_all(target_dir: &Path) -> Result<VerifyReport, VerifyError> {
let format = run_format_check(target_dir)?;
let clippy = run_clippy(target_dir)?;
let test = run_tests(target_dir)?;
Ok(VerifyReport {
format,
clippy,
test,
})
}
fn run_format_check(target_dir: &Path) -> Result<StepResult, VerifyError> {
let output = Command::new("cargo")
.arg("fmt")
.arg("--check")
.current_dir(target_dir)
.output()?;
if output.status.success() {
Ok(StepResult::Pass)
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
let message = format!("{}\n{}", stdout, stderr).trim().to_string();
Ok(StepResult::Fail(message))
}
}
fn run_clippy(target_dir: &Path) -> Result<StepResult, VerifyError> {
let output = Command::new("cargo")
.arg("clippy")
.arg("--all-targets")
.arg("--all-features")
.current_dir(target_dir)
.output()?;
if output.status.success() {
Ok(StepResult::Pass)
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
let message = format!("{}\n{}", stdout, stderr).trim().to_string();
Ok(StepResult::Fail(message))
}
}
fn run_tests(target_dir: &Path) -> Result<StepResult, VerifyError> {
let nextest_check = Command::new("cargo")
.arg("nextest")
.arg("--version")
.output();
match nextest_check {
Ok(output) if output.status.success() => {
let test_output = Command::new("cargo")
.arg("nextest")
.arg("run")
.current_dir(target_dir)
.output()?;
if test_output.status.success() {
Ok(StepResult::Pass)
} else {
let stderr = String::from_utf8_lossy(&test_output.stderr);
let stdout = String::from_utf8_lossy(&test_output.stdout);
let message = format!("{}\n{}", stdout, stderr).trim().to_string();
Ok(StepResult::Fail(message))
}
}
Ok(_) | Err(_) => {
Ok(StepResult::Skip("cargo-nextest not installed".to_string()))
}
}
}