jmap_mail_types/
vacation.rs1use jmap_types::{Id, UTCDate};
7use serde::{Deserialize, Serialize};
8
9#[non_exhaustive]
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct VacationResponse {
17 pub id: Id,
19
20 pub is_enabled: bool,
22
23 #[serde(skip_serializing_if = "Option::is_none")]
26 pub from_date: Option<UTCDate>,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
31 pub to_date: Option<UTCDate>,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
36 pub subject: Option<String>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
41 pub text_body: Option<String>,
42
43 #[serde(skip_serializing_if = "Option::is_none")]
46 pub html_body: Option<String>,
47 #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
52 pub extra: serde_json::Map<String, serde_json::Value>,
53}
54
55impl VacationResponse {
56 pub fn new(id: Id, is_enabled: bool) -> Self {
60 Self {
61 id,
62 is_enabled,
63 from_date: None,
64 to_date: None,
65 subject: None,
66 text_body: None,
67 html_body: None,
68 extra: serde_json::Map::new(),
69 }
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
81 fn vacation_response_full_roundtrip() {
82 let json = r#"{"id":"singleton","isEnabled":true,"fromDate":"2024-06-01T00:00:00Z","toDate":"2024-06-30T23:59:59Z","subject":"Out of office","textBody":"I am out of the office.","htmlBody":"<p>I am out of the office.</p>"}"#;
83 let vr: VacationResponse = serde_json::from_str(json).expect("must parse");
84 assert_eq!(vr.id, "singleton");
85 assert!(vr.is_enabled);
86 assert_eq!(
87 vr.from_date.as_ref().map(|d| d.as_ref()),
88 Some("2024-06-01T00:00:00Z")
89 );
90 assert_eq!(vr.subject.as_deref(), Some("Out of office"));
91 assert_eq!(vr.text_body.as_deref(), Some("I am out of the office."));
92 let back = serde_json::to_string(&vr).expect("serialize");
93 assert_eq!(back, json);
94 }
95
96 #[test]
99 fn vacation_response_minimal_roundtrip() {
100 let json = r#"{"id":"singleton","isEnabled":false}"#;
101 let vr: VacationResponse = serde_json::from_str(json).expect("must parse");
102 assert_eq!(vr.id, "singleton");
103 assert!(!vr.is_enabled);
104 assert!(vr.from_date.is_none());
105 assert!(vr.to_date.is_none());
106 assert!(vr.subject.is_none());
107 assert!(vr.text_body.is_none());
108 assert!(vr.html_body.is_none());
109 let back = serde_json::to_string(&vr).expect("serialize");
110 assert_eq!(back, json);
111 }
112
113 #[test]
118 fn vacation_response_preserves_vendor_extras() {
119 let raw = serde_json::json!({
120 "id": "singleton",
121 "isEnabled": true,
122 "acmeCorpAutoExtend": true
123 });
124 let vr: VacationResponse = serde_json::from_value(raw).unwrap();
125 assert_eq!(
126 vr.extra.get("acmeCorpAutoExtend").and_then(|v| v.as_bool()),
127 Some(true)
128 );
129 let back = serde_json::to_value(&vr).unwrap();
130 assert_eq!(back["acmeCorpAutoExtend"], true);
131 }
132
133 #[test]
136 fn vacation_response_none_fields_omitted() {
137 let vr = VacationResponse {
138 id: Id::from("singleton"),
139 is_enabled: false,
140 from_date: None,
141 to_date: None,
142 subject: None,
143 text_body: None,
144 html_body: None,
145 extra: serde_json::Map::new(),
146 };
147 let json = serde_json::to_string(&vr).expect("serialize");
148 assert!(!json.contains("fromDate"), "fromDate must be absent");
149 assert!(!json.contains("toDate"), "toDate must be absent");
150 assert!(!json.contains("subject"), "subject must be absent");
151 assert!(!json.contains("textBody"), "textBody must be absent");
152 assert!(!json.contains("htmlBody"), "htmlBody must be absent");
153 }
154}