use crate::checks::{CheckResult, Status};
fn penalty(id: &str, status: &Status) -> u32 {
match (id, status) {
("signature", Status::Fail) => 100, ("timestamp", Status::Fail) => 40,
("digest", Status::Fail) => 30,
("chain", Status::Fail) => 40,
("expiry", Status::Fail) => 20,
("expiry", Status::Warn) => 10,
_ => 0,
}
}
pub fn score(results: &[CheckResult]) -> u32 {
let total: u32 = results.iter().map(|c| penalty(&c.id, &c.status)).sum();
100u32.saturating_sub(total)
}
pub fn grade(score: u32) -> char {
match score {
90..=100 => 'A',
80..=89 => 'B',
70..=79 => 'C',
60..=69 => 'D',
_ => 'F',
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::checks::{CheckResult, Status};
fn c(id: &str, s: Status) -> CheckResult {
CheckResult {
id: id.into(),
status: s,
detail: String::new(),
}
}
#[test]
fn clean_is_a() {
let r = vec![
c("signature", Status::Pass),
c("timestamp", Status::Pass),
c("digest", Status::Pass),
c("expiry", Status::Pass),
];
assert_eq!(score(&r), 100);
assert_eq!(grade(100), 'A');
}
#[test]
fn unsigned_is_f() {
let r = vec![c("signature", Status::Fail)];
assert_eq!(score(&r), 0);
assert_eq!(grade(score(&r)), 'F');
}
#[test]
fn missing_timestamp_drops_to_d() {
let r = vec![
c("signature", Status::Pass),
c("timestamp", Status::Fail),
c("digest", Status::Pass),
c("expiry", Status::Pass),
];
assert_eq!(score(&r), 60); assert_eq!(grade(60), 'D');
}
}