use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct VerificationResult {
pub valid: bool,
pub checks: Vec<PathFailure>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum PathFailure {
Expired,
NotYetValid,
SignatureInvalid,
ScopeViolation {
expected: String,
actual: String,
},
ChainTooLong,
UntrustedRoot,
Revoked {
crl_url: String,
serial: String,
},
}
impl VerificationResult {
pub fn aggregate(results: &[VerificationResult]) -> Self {
let mut combined = VerificationResult {
valid: true,
checks: Vec::new(),
};
for r in results {
if !r.valid {
combined.valid = false;
}
combined.checks.extend(r.checks.iter().cloned());
}
combined
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn aggregate_passing_results() {
let r1 = VerificationResult {
valid: true,
checks: vec![],
};
let r2 = VerificationResult {
valid: true,
checks: vec![],
};
let combined = VerificationResult::aggregate(&[r1, r2]);
assert!(combined.valid);
assert!(combined.checks.is_empty());
}
#[test]
fn aggregate_with_failure_propagates() {
let r1 = VerificationResult {
valid: true,
checks: vec![],
};
let r2 = VerificationResult {
valid: false,
checks: vec![PathFailure::Expired],
};
let combined = VerificationResult::aggregate(&[r1, r2]);
assert!(!combined.valid);
assert_eq!(combined.checks.len(), 1);
}
}