use bestool_alertd::doctor::check::{Check, CheckStatus};
pub fn severity_key(status: &CheckStatus) -> u8 {
match status {
CheckStatus::Pass => 0,
CheckStatus::Skip(_) => 1,
CheckStatus::Warning(_) => 2,
CheckStatus::Broken(_) => 3,
CheckStatus::Fail(_) => 4,
}
}
pub fn keep_under_filter(status: &CheckStatus, only_failing: bool) -> bool {
if !only_failing {
return true;
}
matches!(
status,
CheckStatus::Warning(_) | CheckStatus::Broken(_) | CheckStatus::Fail(_)
)
}
pub fn sort_grouped(results: &mut [(Check, bool)]) {
results.sort_by(|a, b| {
severity_key(&a.0.status)
.cmp(&severity_key(&b.0.status))
.then_with(|| a.0.name.cmp(b.0.name))
});
}
pub fn filter_and_sort(results: &[(Check, bool)], only_failing: bool) -> Vec<(Check, bool)> {
let mut out: Vec<(Check, bool)> = results
.iter()
.filter(|(c, _)| keep_under_filter(&c.status, only_failing))
.cloned()
.collect();
sort_grouped(&mut out);
out
}
#[cfg(test)]
mod tests {
use super::*;
fn pass(name: &'static str) -> (Check, bool) {
(Check::pass(name, "ok"), true)
}
fn warn(name: &'static str) -> (Check, bool) {
(Check::warning(name, "deg", "reason"), true)
}
fn broken(name: &'static str) -> (Check, bool) {
(Check::broken(name, "broke", "reason"), true)
}
fn fail(name: &'static str) -> (Check, bool) {
(Check::fail(name, "bad", "reason"), true)
}
fn skip(name: &'static str) -> (Check, bool) {
(Check::skip(name, "not run", "reason"), true)
}
#[test]
fn severity_key_orders_least_to_most() {
assert!(severity_key(&CheckStatus::Pass) < severity_key(&CheckStatus::Skip("".into())));
assert!(
severity_key(&CheckStatus::Skip("".into()))
< severity_key(&CheckStatus::Warning("".into()))
);
assert!(
severity_key(&CheckStatus::Warning("".into()))
< severity_key(&CheckStatus::Broken("".into()))
);
assert!(
severity_key(&CheckStatus::Broken("".into()))
< severity_key(&CheckStatus::Fail("".into()))
);
}
#[test]
fn sort_grouped_pass_skip_warn_broken_fail_alphabetical() {
let mut results = vec![
fail("zebra"),
pass("delta"),
warn("alpha"),
broken("beta"),
skip("gamma"),
pass("charlie"),
fail("apple"),
warn("yak"),
];
sort_grouped(&mut results);
let names: Vec<&str> = results.iter().map(|(c, _)| c.name).collect();
assert_eq!(
names,
vec!["charlie", "delta", "gamma", "alpha", "yak", "beta", "apple", "zebra"]
);
}
#[test]
fn keep_under_filter_hides_pass_and_skip() {
assert!(!keep_under_filter(&CheckStatus::Pass, true));
assert!(!keep_under_filter(&CheckStatus::Skip("".into()), true));
assert!(keep_under_filter(&CheckStatus::Warning("".into()), true));
assert!(keep_under_filter(&CheckStatus::Broken("".into()), true));
assert!(keep_under_filter(&CheckStatus::Fail("".into()), true));
}
#[test]
fn keep_under_filter_off_keeps_everything() {
assert!(keep_under_filter(&CheckStatus::Pass, false));
assert!(keep_under_filter(&CheckStatus::Skip("".into()), false));
assert!(keep_under_filter(&CheckStatus::Warning("".into()), false));
assert!(keep_under_filter(&CheckStatus::Broken("".into()), false));
assert!(keep_under_filter(&CheckStatus::Fail("".into()), false));
}
#[test]
fn filter_and_sort_with_only_failing_drops_pass_and_skip() {
let input = vec![
pass("a"),
warn("b"),
skip("c"),
fail("d"),
broken("e"),
pass("f"),
];
let out = filter_and_sort(&input, true);
let names: Vec<&str> = out.iter().map(|(c, _)| c.name).collect();
assert_eq!(names, vec!["b", "e", "d"]);
}
}