1use std::str::FromStr;
2
3use serde::{Deserialize, Serialize};
4
5use crate::Override;
6#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Copy, Clone, Serialize, Deserialize, Hash)]
7#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
8#[serde(rename_all = "UPPERCASE")]
9pub enum StatusCode {
11 Skip,
13 Pass,
15 Info,
17 Warn,
19 Fail,
21 Error,
29}
30
31impl FromStr for StatusCode {
32 type Err = ();
33
34 fn from_str(s: &str) -> Result<Self, Self::Err> {
35 match s {
36 "SKIP" => Ok(StatusCode::Skip),
37 "INFO" => Ok(StatusCode::Info),
38 "PASS" => Ok(StatusCode::Pass),
39 "WARN" => Ok(StatusCode::Warn),
40 "FAIL" => Ok(StatusCode::Fail),
41 "ERROR" => Ok(StatusCode::Error),
42 _ => Err(()),
43 }
44 }
45}
46
47impl StatusCode {
48 pub fn all() -> impl Iterator<Item = StatusCode> {
53 vec![
54 StatusCode::Skip,
55 StatusCode::Info,
56 StatusCode::Pass,
57 StatusCode::Warn,
58 StatusCode::Fail,
59 StatusCode::Error,
60 ]
61 .into_iter()
62 }
63
64 pub fn from_string(s: &str) -> Option<StatusCode> {
69 FromStr::from_str(s).ok()
70 }
71}
72impl std::fmt::Display for StatusCode {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 match *self {
75 StatusCode::Pass => write!(f, "PASS"),
76 StatusCode::Skip => write!(f, "SKIP"),
77 StatusCode::Fail => write!(f, "FAIL"),
78 StatusCode::Warn => write!(f, "WARN"),
79 StatusCode::Info => write!(f, "INFO"),
80 StatusCode::Error => write!(f, "ERROR"),
81 }
82 }
83}
84#[derive(Debug, Clone, Serialize)]
85pub struct Status {
91 #[serde(skip_serializing_if = "Option::is_none")]
93 pub message: Option<String>,
94 pub severity: StatusCode,
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub code: Option<String>,
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub metadata: Option<serde_json::Value>,
102}
103
104impl std::fmt::Display for Status {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 write!(f, "**{:}**: ", self.severity)?;
107 if let Some(code) = self.code.as_ref() {
108 write!(f, "[{}]: ", code)?;
109 }
110 if let Some(message) = self.message.as_ref() {
111 write!(f, "{:}", message)?;
112 }
113 Ok(())
114 }
115}
116
117impl Status {
118 pub fn just_one_pass() -> Box<dyn Iterator<Item = Status>> {
120 Box::new(vec![Status::pass()].into_iter())
121 }
122
123 pub fn just_one_warn(code: &str, message: &str) -> Box<dyn Iterator<Item = Status>> {
125 Box::new(vec![Status::warn(code, message)].into_iter())
126 }
127
128 pub fn just_one_info(code: &str, message: &str) -> Box<dyn Iterator<Item = Status>> {
130 Box::new(vec![Status::info(code, message)].into_iter())
131 }
132
133 pub fn just_one_fail(code: &str, message: &str) -> Box<dyn Iterator<Item = Status>> {
135 Box::new(vec![Status::fail(code, message)].into_iter())
136 }
137
138 pub fn just_one_skip(code: &str, message: &str) -> Box<dyn Iterator<Item = Status>> {
140 Box::new(vec![Status::skip(code, message)].into_iter())
141 }
142
143 pub fn pass() -> Self {
145 Self {
146 message: None,
147 code: None,
148 severity: StatusCode::Pass,
149 metadata: None,
150 }
151 }
152 pub fn fail(code: &str, message: &str) -> Self {
154 Self {
155 message: Some(message.to_string()),
156 code: Some(code.to_string()),
157 severity: StatusCode::Fail,
158 metadata: None,
159 }
160 }
161 pub fn warn(code: &str, message: &str) -> Self {
163 Self {
164 message: Some(message.to_string()),
165 code: Some(code.to_string()),
166 severity: StatusCode::Warn,
167 metadata: None,
168 }
169 }
170 pub fn skip(code: &str, message: &str) -> Self {
172 Self {
173 message: Some(message.to_string()),
174 code: Some(code.to_string()),
175 severity: StatusCode::Skip,
176 metadata: None,
177 }
178 }
179 pub fn info(code: &str, message: &str) -> Self {
181 Self {
182 message: Some(message.to_string()),
183 code: Some(code.to_string()),
184 severity: StatusCode::Info,
185 metadata: None,
186 }
187 }
188 pub fn error(code: Option<&str>, message: &str) -> Self {
190 Self {
191 message: Some(message.to_string()),
192 code: code.map(|x| x.to_string()),
193 severity: StatusCode::Error,
194 metadata: None,
195 }
196 }
197
198 pub fn process_override(&mut self, overrides: &[Override]) {
203 let code = self.code.as_deref();
204 if let Some(override_) = overrides.iter().find(|x| Some(x.code.as_str()) == code) {
205 self.severity = override_.status;
206 self.message = Some(format!(
207 "{} (Overriden: {})",
208 self.message
209 .clone()
210 .unwrap_or("No original message".to_string()),
211 override_.reason
212 ))
213 }
214 }
215}
216
217#[derive(Debug)]
221pub enum CheckError {
222 Error(String),
224 Skip {
226 code: String,
228 message: String,
230 },
231}
232
233impl<T> From<T> for CheckError
234where
235 T: std::error::Error,
236{
237 fn from(e: T) -> Self {
238 CheckError::Error(e.to_string())
239 }
240}
241
242impl CheckError {
243 pub fn skip(code: &str, message: &str) -> Self {
248 CheckError::Skip {
249 code: code.to_string(),
250 message: message.to_string(),
251 }
252 }
253}
254
255pub type StatusList = Box<dyn Iterator<Item = Status>>;
257pub type CheckFnResult = Result<StatusList, CheckError>;