#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
mod baseline;
mod capture;
mod report;
mod scenario;
mod scratch;
mod witness;
pub use baseline::{
assert_no_worse_than, compare_to_baseline, Baseline, BaselineEntry, UPDATE_ENV,
};
pub use capture::{CapturedFile, CrashState, Tier, Warrant};
pub use report::{Checker, Finding, IsAbsent, Report, Verdict, Violation, ViolationKind};
pub use scenario::{Acked, Scenario};
pub use scratch::ScratchDir;
pub use witness::{
AssumedDurable, Barrier, Coverage, DurabilityWitness, SyncJournal, SyncKind, SyncRecord,
};
use std::path::PathBuf;
pub type Result<T> = std::result::Result<T, Error>;
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
StoreMissing(PathBuf),
Io {
path: Option<PathBuf>,
source: std::io::Error,
},
}
impl Error {
pub(crate) fn io(path: Option<PathBuf>, source: std::io::Error) -> Self {
Error::Io { path, source }
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::StoreMissing(p) => write!(
f,
"the store directory `{}` does not exist. `Scenario::new` wants the directory \
your engine keeps its files in, and it must exist before the workload runs.",
p.display()
),
Error::Io {
path: Some(p),
source,
} => write!(f, "i/o error at `{}`: {source}", p.display()),
Error::Io { path: None, source } => write!(f, "i/o error: {source}"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Io { source, .. } => Some(source),
Error::StoreMissing(_) => None,
}
}
}
impl From<std::io::Error> for Error {
fn from(source: std::io::Error) -> Self {
Error::Io { path: None, source }
}
}
#[must_use]
pub fn to_markdown(reports: &[Report]) -> String {
let mut out = String::from("| case | tier | verdict | detail |\n|---|---|---|---|\n");
for report in reports {
out.push_str(&report.to_markdown_row());
out.push('\n');
}
out
}
pub fn assert_all_survived(reports: &[Report]) {
for report in reports {
println!("{}", report.to_text());
}
let failed: Vec<&Report> = reports.iter().filter(|r| !r.verdict().is_pass()).collect();
assert!(
failed.is_empty(),
"\npowercut: {} of {} case(s) did not survive:\n\n{}\n",
failed.len(),
reports.len(),
failed
.iter()
.map(|r| r.to_text())
.collect::<Vec<_>>()
.join("\n")
);
}