1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7pub struct VerificationResult {
8 pub valid: bool,
10 pub checks: Vec<PathFailure>,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16#[serde(tag = "type", rename_all = "snake_case")]
17pub enum PathFailure {
18 Expired,
20 NotYetValid,
22 SignatureInvalid,
24 ScopeViolation {
26 expected: String,
28 actual: String,
30 },
31 ChainTooLong,
33 UntrustedRoot,
35 Revoked {
37 crl_url: String,
39 serial: String,
41 },
42}
43
44impl VerificationResult {
45 pub fn aggregate(results: &[VerificationResult]) -> Self {
47 let mut combined = VerificationResult {
48 valid: true,
49 checks: Vec::new(),
50 };
51 for r in results {
52 if !r.valid {
53 combined.valid = false;
54 }
55 combined.checks.extend(r.checks.iter().cloned());
56 }
57 combined
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn aggregate_passing_results() {
67 let r1 = VerificationResult {
68 valid: true,
69 checks: vec![],
70 };
71 let r2 = VerificationResult {
72 valid: true,
73 checks: vec![],
74 };
75 let combined = VerificationResult::aggregate(&[r1, r2]);
76 assert!(combined.valid);
77 assert!(combined.checks.is_empty());
78 }
79
80 #[test]
81 fn aggregate_with_failure_propagates() {
82 let r1 = VerificationResult {
83 valid: true,
84 checks: vec![],
85 };
86 let r2 = VerificationResult {
87 valid: false,
88 checks: vec![PathFailure::Expired],
89 };
90 let combined = VerificationResult::aggregate(&[r1, r2]);
91 assert!(!combined.valid);
92 assert_eq!(combined.checks.len(), 1);
93 }
94}