wavyzon_plugin/
failure.rs1use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "lowercase")]
8pub enum Severity {
9 Common,
11 Suspicious,
13 Fault,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(rename_all = "camelCase")]
19pub struct Failure {
20 pub severity: Severity,
21 pub message: String,
22 #[serde(default, skip_serializing_if = "String::is_empty")]
23 pub cause: String,
24 #[serde(default, skip_serializing_if = "String::is_empty")]
25 pub source: String,
26}
27
28impl Failure {
29 pub fn new(
30 severity: Severity,
31 source: impl Into<String>,
32 message: impl Into<String>,
33 cause: Option<String>,
34 ) -> Self {
35 Self {
36 severity,
37 message: message.into(),
38 cause: cause.unwrap_or_default(),
39 source: source.into(),
40 }
41 }
42 pub fn common(source: impl Into<String>, message: impl Into<String>, cause: Option<String>) -> Self {
43 Self::new(Severity::Common, source, message, cause)
44 }
45 pub fn suspicious(source: impl Into<String>, message: impl Into<String>, cause: Option<String>) -> Self {
46 Self::new(Severity::Suspicious, source, message, cause)
47 }
48 pub fn fault(source: impl Into<String>, message: impl Into<String>, cause: Option<String>) -> Self {
49 Self::new(Severity::Fault, source, message, cause)
50 }
51
52 pub fn from_status(source: &str, status: u16, cause: impl Into<String>) -> Self {
54 let cause = Some(cause.into());
55 match status {
56 401 | 403 | 429 => Self::suspicious(source, "refused by source", cause),
57 404 => Self::common(source, "not found at source", cause),
58 _ => Self::common(source, format!("{source} failed"), cause),
59 }
60 }
61}
62
63impl std::fmt::Display for Failure {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 if self.cause.is_empty() {
66 f.write_str(&self.message)
67 } else {
68 write!(f, "{}: {}", self.message, self.cause)
69 }
70 }
71}
72
73impl std::error::Error for Failure {}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn status_classification() {
81 assert_eq!(Failure::from_status("x", 429, "").severity, Severity::Suspicious);
82 assert_eq!(Failure::from_status("x", 404, "").message, "not found at source");
83 assert_eq!(
84 Failure::common("x", "boom", Some("why".into())).to_string(),
85 "boom: why"
86 );
87 }
88}