1use serde::{Deserialize, Deserializer};
4use std::fmt;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
8pub enum PatchStatus {
9 #[default]
11 Pending,
12 Reviewed,
14 Failed,
16 ApplyError,
18 Unknown,
20}
21
22impl fmt::Display for PatchStatus {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 match self {
25 Self::Pending => write!(f, "Pending"),
26 Self::Reviewed => write!(f, "Reviewed"),
27 Self::Failed => write!(f, "Failed"),
28 Self::ApplyError => write!(f, "Apply Error"),
29 Self::Unknown => write!(f, "Unknown"),
30 }
31 }
32}
33
34impl<'de> Deserialize<'de> for PatchStatus {
35 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
36 where
37 D: Deserializer<'de>,
38 {
39 let s = Option::<String>::deserialize(deserializer)?;
40 Ok(match s.as_deref().map(str::to_lowercase).as_deref() {
41 Some("reviewed") => Self::Reviewed,
42 Some("failed") => Self::Failed,
43 Some("apply error" | "applyerror" | "failedtoapply") => Self::ApplyError,
44 Some("pending") | None => Self::Pending,
45 Some(other) => {
46 tracing::warn!(status = other, "unknown patch status, using Unknown");
47 Self::Unknown
48 }
49 })
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, Deserialize)]
55pub struct Patch {
56 #[serde(default)]
58 pub id: i64,
59 #[serde(default)]
61 pub message_id: Option<String>,
62 #[serde(default)]
64 pub part_index: Option<u32>,
65 #[serde(default)]
67 pub subject: Option<String>,
68 #[serde(default)]
70 pub status: Option<PatchStatus>,
71 #[serde(default)]
73 pub apply_error: Option<String>,
74 #[serde(default)]
76 pub msg_db_id: Option<i64>,
77}
78
79#[cfg(test)]
80#[allow(clippy::expect_used)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn patch_status_default() {
86 assert_eq!(PatchStatus::default(), PatchStatus::Pending);
87 }
88
89 #[test]
90 fn patch_status_display() {
91 assert_eq!(PatchStatus::ApplyError.to_string(), "Apply Error");
92 assert_eq!(PatchStatus::Unknown.to_string(), "Unknown");
93 }
94
95 #[test]
96 fn deserialize_patch() {
97 let json = r#"{"id": 42, "part_index": 3, "subject": "fix something"}"#;
98 let patch: Patch = serde_json::from_str(json).expect("deserialize patch");
99 assert_eq!(patch.id, 42);
100 assert_eq!(patch.part_index, Some(3));
101 }
102
103 #[test]
104 fn deserialize_patch_unknown_status() {
105 let json = r#"{"id": 1, "status": "NewThing"}"#;
106 let patch: Patch = serde_json::from_str(json).expect("deserialize patch unknown status");
107 assert_eq!(patch.status, Some(PatchStatus::Unknown));
108 }
109}