1use anyhow::Result;
2use assay_evidence::bundle::writer::{ErrorClass, ErrorCode};
3use serde::Serialize;
4
5#[derive(Debug, Serialize, Clone)]
6pub struct SimReport {
7 pub suite: String,
8 pub seed: u64,
9 pub summary: SimSummary,
10 pub results: Vec<AttackResult>,
11}
12
13#[derive(Debug, Serialize, Clone, Default)]
14pub struct SimSummary {
15 pub total: usize,
16 pub passed: usize, pub blocked: usize, pub bypassed: usize, pub failed: usize, pub errors: usize,
21}
22
23#[derive(Debug, Serialize, Clone)]
24pub struct AttackResult {
25 pub name: String,
26 pub status: AttackStatus,
27 pub error_class: Option<String>,
28 pub error_code: Option<String>,
29 pub message: Option<String>,
30 pub duration_ms: u64, }
32
33#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
34pub enum AttackStatus {
35 Passed, Failed, Blocked, Bypassed, Error, }
41
42impl SimReport {
43 pub fn new(suite: &str, seed: u64) -> Self {
44 Self {
45 suite: suite.to_string(),
46 seed,
47 summary: SimSummary::default(),
48 results: Vec::new(),
49 }
50 }
51
52 pub fn add_attack(
53 &mut self,
54 name: &str,
55 result: Result<(ErrorClass, ErrorCode), anyhow::Error>,
56 duration_ms: u64,
57 ) {
58 self.summary.total += 1;
59 let res = match result {
60 Ok((class, code)) => {
61 self.summary.blocked += 1;
62 AttackResult {
63 name: name.to_string(),
64 status: AttackStatus::Blocked,
65 error_class: Some(format!("{:?}", class)),
66 error_code: Some(format!("{:?}", code)),
67 message: None,
68 duration_ms,
69 }
70 }
71 Err(e) => {
72 self.summary.bypassed += 1;
73 AttackResult {
74 name: name.to_string(),
75 status: AttackStatus::Bypassed,
76 error_class: None,
77 error_code: None,
78 message: Some(e.to_string()),
79 duration_ms,
80 }
81 }
82 };
83 self.results.push(res);
84 }
85
86 pub fn add_result(&mut self, result: AttackResult) {
88 self.summary.total += 1;
89 match result.status {
90 AttackStatus::Passed => self.summary.passed += 1,
91 AttackStatus::Failed => self.summary.failed += 1,
92 AttackStatus::Blocked => self.summary.blocked += 1,
93 AttackStatus::Bypassed => self.summary.bypassed += 1,
94 AttackStatus::Error => self.summary.errors += 1,
95 }
96 self.results.push(result);
97 }
98
99 pub fn add_check(&mut self, name: &str, result: Result<()>, duration_ms: u64) {
100 self.summary.total += 1;
101 let res = match result {
102 Ok(_) => {
103 self.summary.passed += 1;
104 AttackResult {
105 name: name.to_string(),
106 status: AttackStatus::Passed,
107 error_class: None,
108 error_code: None,
109 message: None,
110 duration_ms,
111 }
112 }
113 Err(e) => {
114 self.summary.failed += 1;
115 AttackResult {
116 name: name.to_string(),
117 status: AttackStatus::Failed,
118 error_class: None,
119 error_code: None,
120 message: Some(e.to_string()),
121 duration_ms,
122 }
123 }
124 };
125 self.results.push(res);
126 }
127}