1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, Deserialize, Serialize)]
9#[serde(rename_all = "camelCase")]
10pub struct JmapRequest {
11 pub using: Vec<String>,
12 pub method_calls: Vec<JmapMethodCall>,
13 #[serde(skip_serializing_if = "Option::is_none")]
14 pub created_ids: Option<serde_json::Value>,
15}
16
17#[derive(Debug, Clone, Deserialize, Serialize)]
19pub struct JmapMethodCall(pub String, pub serde_json::Value, pub String);
20
21#[derive(Debug, Clone, Default, Deserialize, Serialize)]
23#[serde(rename_all = "camelCase")]
24pub struct JmapResponse {
25 pub method_responses: Vec<JmapMethodResponse>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub session_state: Option<String>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub created_ids: Option<serde_json::Value>,
30}
31
32#[derive(Debug, Clone, Deserialize, Serialize)]
34pub struct JmapMethodResponse(pub String, pub serde_json::Value, pub String);
35
36#[derive(Debug, Clone)]
38pub enum JmapMethod {
39 EmailGet,
41 EmailSet,
43 EmailQuery,
45 MailboxGet,
47 MailboxSet,
49 MailboxQuery,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
55pub enum JmapErrorType {
56 NotJson,
58 NotRequest,
60 Limit,
62 UnknownCapability,
64 UnknownMethod,
66 InvalidArguments,
68 AccountNotFound,
70 AccountNotSupportedByMethod,
72 AccountReadOnly,
74 ServerFail,
76 ServerUnavailable,
78 ServerPartialFailure,
80}
81
82impl JmapErrorType {
83 pub fn as_str(&self) -> &'static str {
85 match self {
86 Self::NotJson => "urn:ietf:params:jmap:error:notJSON",
87 Self::NotRequest => "urn:ietf:params:jmap:error:notRequest",
88 Self::Limit => "urn:ietf:params:jmap:error:limit",
89 Self::UnknownCapability => "urn:ietf:params:jmap:error:unknownCapability",
90 Self::UnknownMethod => "urn:ietf:params:jmap:error:unknownMethod",
91 Self::InvalidArguments => "urn:ietf:params:jmap:error:invalidArguments",
92 Self::AccountNotFound => "urn:ietf:params:jmap:error:accountNotFound",
93 Self::AccountNotSupportedByMethod => {
94 "urn:ietf:params:jmap:error:accountNotSupportedByMethod"
95 }
96 Self::AccountReadOnly => "urn:ietf:params:jmap:error:accountReadOnly",
97 Self::ServerFail => "urn:ietf:params:jmap:error:serverFail",
98 Self::ServerUnavailable => "urn:ietf:params:jmap:error:serverUnavailable",
99 Self::ServerPartialFailure => "urn:ietf:params:jmap:error:serverPartialFailure",
100 }
101 }
102}
103
104#[derive(Debug, Clone, Serialize)]
106pub struct JmapError {
107 #[serde(rename = "type")]
108 pub error_type: String,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub status: Option<u16>,
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub detail: Option<String>,
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub limit: Option<String>,
115}
116
117impl JmapError {
118 pub fn new(error_type: JmapErrorType) -> Self {
120 Self {
121 error_type: error_type.as_str().to_string(),
122 status: None,
123 detail: None,
124 limit: None,
125 }
126 }
127
128 pub fn with_status(mut self, status: u16) -> Self {
130 self.status = Some(status);
131 self
132 }
133
134 pub fn with_detail(mut self, detail: impl Into<String>) -> Self {
136 self.detail = Some(detail.into());
137 self
138 }
139
140 pub fn with_limit(mut self, limit: impl Into<String>) -> Self {
142 self.limit = Some(limit.into());
143 self
144 }
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct EmailAddress {
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub name: Option<String>,
152 pub email: String,
153}
154
155impl EmailAddress {
156 pub fn new(email: String) -> Self {
158 Self { name: None, email }
159 }
160
161 pub fn with_name(email: String, name: String) -> Self {
163 Self {
164 name: Some(name),
165 email,
166 }
167 }
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172#[serde(rename_all = "camelCase")]
173pub struct Email {
174 pub id: String,
176 pub blob_id: String,
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub thread_id: Option<String>,
181 pub mailbox_ids: HashMap<String, bool>,
183 pub keywords: HashMap<String, bool>,
185 pub size: u64,
187 pub received_at: DateTime<Utc>,
189 #[serde(skip_serializing_if = "Option::is_none")]
191 pub message_id: Option<Vec<String>>,
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub in_reply_to: Option<Vec<String>>,
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub references: Option<Vec<String>>,
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub sender: Option<Vec<EmailAddress>>,
201 #[serde(skip_serializing_if = "Option::is_none")]
203 pub from: Option<Vec<EmailAddress>>,
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub to: Option<Vec<EmailAddress>>,
207 #[serde(skip_serializing_if = "Option::is_none")]
209 pub cc: Option<Vec<EmailAddress>>,
210 #[serde(skip_serializing_if = "Option::is_none")]
212 pub bcc: Option<Vec<EmailAddress>>,
213 #[serde(skip_serializing_if = "Option::is_none")]
215 pub reply_to: Option<Vec<EmailAddress>>,
216 #[serde(skip_serializing_if = "Option::is_none")]
218 pub subject: Option<String>,
219 #[serde(skip_serializing_if = "Option::is_none")]
221 pub sent_at: Option<DateTime<Utc>>,
222 pub has_attachment: bool,
224 #[serde(skip_serializing_if = "Option::is_none")]
226 pub preview: Option<String>,
227 #[serde(skip_serializing_if = "Option::is_none")]
229 pub body_values: Option<HashMap<String, EmailBodyValue>>,
230 #[serde(skip_serializing_if = "Option::is_none")]
232 pub text_body: Option<Vec<EmailBodyPart>>,
233 #[serde(skip_serializing_if = "Option::is_none")]
235 pub html_body: Option<Vec<EmailBodyPart>>,
236 #[serde(skip_serializing_if = "Option::is_none")]
238 pub attachments: Option<Vec<EmailBodyPart>>,
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub struct EmailBodyValue {
245 pub value: String,
246 pub is_encoding_problem: bool,
247 pub is_truncated: bool,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
252#[serde(rename_all = "camelCase")]
253pub struct EmailBodyPart {
254 pub part_id: String,
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub blob_id: Option<String>,
257 #[serde(skip_serializing_if = "Option::is_none")]
258 pub size: Option<u64>,
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub name: Option<String>,
261 #[serde(skip_serializing_if = "Option::is_none")]
262 pub r#type: Option<String>,
263 #[serde(skip_serializing_if = "Option::is_none")]
264 pub charset: Option<String>,
265 #[serde(skip_serializing_if = "Option::is_none")]
266 pub disposition: Option<String>,
267 #[serde(skip_serializing_if = "Option::is_none")]
268 pub cid: Option<String>,
269 #[serde(skip_serializing_if = "Option::is_none")]
270 pub language: Option<Vec<String>>,
271 #[serde(skip_serializing_if = "Option::is_none")]
272 pub location: Option<String>,
273}
274
275#[derive(Debug, Clone, Deserialize)]
277#[serde(rename_all = "camelCase")]
278pub struct EmailGetRequest {
279 pub account_id: String,
280 #[serde(skip_serializing_if = "Option::is_none")]
281 pub ids: Option<Vec<String>>,
282 #[serde(skip_serializing_if = "Option::is_none")]
283 pub properties: Option<Vec<String>>,
284 #[serde(skip_serializing_if = "Option::is_none")]
285 pub body_properties: Option<Vec<String>>,
286 #[serde(skip_serializing_if = "Option::is_none")]
287 pub fetch_text_body_values: Option<bool>,
288 #[serde(skip_serializing_if = "Option::is_none")]
289 pub fetch_html_body_values: Option<bool>,
290 #[serde(skip_serializing_if = "Option::is_none")]
291 pub fetch_all_body_values: Option<bool>,
292 #[serde(skip_serializing_if = "Option::is_none")]
293 pub max_body_value_bytes: Option<u64>,
294}
295
296#[derive(Debug, Clone, Serialize)]
298#[serde(rename_all = "camelCase")]
299pub struct EmailGetResponse {
300 pub account_id: String,
301 pub state: String,
302 pub list: Vec<Email>,
303 pub not_found: Vec<String>,
304}
305
306#[derive(Debug, Clone, Deserialize)]
308#[serde(rename_all = "camelCase")]
309pub struct EmailSetRequest {
310 pub account_id: String,
311 #[serde(skip_serializing_if = "Option::is_none")]
312 pub if_in_state: Option<String>,
313 #[serde(skip_serializing_if = "Option::is_none")]
314 pub create: Option<HashMap<String, EmailSetObject>>,
315 #[serde(skip_serializing_if = "Option::is_none")]
316 pub update: Option<HashMap<String, serde_json::Value>>,
317 #[serde(skip_serializing_if = "Option::is_none")]
318 pub destroy: Option<Vec<String>>,
319}
320
321#[derive(Debug, Clone, Deserialize)]
323#[serde(rename_all = "camelCase")]
324pub struct EmailSetObject {
325 pub mailbox_ids: HashMap<String, bool>,
326 #[serde(skip_serializing_if = "Option::is_none")]
327 pub keywords: Option<HashMap<String, bool>>,
328 #[serde(skip_serializing_if = "Option::is_none")]
329 pub received_at: Option<DateTime<Utc>>,
330}
331
332#[derive(Debug, Clone, Serialize)]
334#[serde(rename_all = "camelCase")]
335pub struct EmailSetResponse {
336 pub account_id: String,
337 pub old_state: String,
338 pub new_state: String,
339 #[serde(skip_serializing_if = "Option::is_none")]
340 pub created: Option<HashMap<String, Email>>,
341 #[serde(skip_serializing_if = "Option::is_none")]
342 pub updated: Option<HashMap<String, Option<Email>>>,
343 #[serde(skip_serializing_if = "Option::is_none")]
344 pub destroyed: Option<Vec<String>>,
345 #[serde(skip_serializing_if = "Option::is_none")]
346 pub not_created: Option<HashMap<String, JmapSetError>>,
347 #[serde(skip_serializing_if = "Option::is_none")]
348 pub not_updated: Option<HashMap<String, JmapSetError>>,
349 #[serde(skip_serializing_if = "Option::is_none")]
350 pub not_destroyed: Option<HashMap<String, JmapSetError>>,
351}
352
353#[derive(Debug, Clone, Serialize)]
355pub struct JmapSetError {
356 #[serde(rename = "type")]
357 pub error_type: String,
358 #[serde(skip_serializing_if = "Option::is_none")]
359 pub description: Option<String>,
360}
361
362#[derive(Debug, Clone, Deserialize)]
364#[serde(rename_all = "camelCase")]
365pub struct EmailQueryRequest {
366 pub account_id: String,
367 #[serde(skip_serializing_if = "Option::is_none")]
368 pub filter: Option<EmailFilterCondition>,
369 #[serde(skip_serializing_if = "Option::is_none")]
370 pub sort: Option<Vec<EmailSort>>,
371 #[serde(skip_serializing_if = "Option::is_none")]
372 pub position: Option<i64>,
373 #[serde(skip_serializing_if = "Option::is_none")]
374 pub anchor: Option<String>,
375 #[serde(skip_serializing_if = "Option::is_none")]
376 pub anchor_offset: Option<i64>,
377 #[serde(skip_serializing_if = "Option::is_none")]
378 pub limit: Option<u64>,
379 #[serde(skip_serializing_if = "Option::is_none")]
380 pub calculate_total: Option<bool>,
381}
382
383#[derive(Debug, Clone, Deserialize)]
385#[serde(rename_all = "camelCase")]
386pub struct EmailFilterCondition {
387 #[serde(skip_serializing_if = "Option::is_none")]
388 pub in_mailbox: Option<String>,
389 #[serde(skip_serializing_if = "Option::is_none")]
390 pub in_mailbox_other_than: Option<Vec<String>>,
391 #[serde(skip_serializing_if = "Option::is_none")]
392 pub before: Option<DateTime<Utc>>,
393 #[serde(skip_serializing_if = "Option::is_none")]
394 pub after: Option<DateTime<Utc>>,
395 #[serde(skip_serializing_if = "Option::is_none")]
396 pub min_size: Option<u64>,
397 #[serde(skip_serializing_if = "Option::is_none")]
398 pub max_size: Option<u64>,
399 #[serde(skip_serializing_if = "Option::is_none")]
400 pub all_in_thread_have_keyword: Option<String>,
401 #[serde(skip_serializing_if = "Option::is_none")]
402 pub some_in_thread_have_keyword: Option<String>,
403 #[serde(skip_serializing_if = "Option::is_none")]
404 pub none_in_thread_have_keyword: Option<String>,
405 #[serde(skip_serializing_if = "Option::is_none")]
406 pub has_keyword: Option<String>,
407 #[serde(skip_serializing_if = "Option::is_none")]
408 pub not_keyword: Option<String>,
409 #[serde(skip_serializing_if = "Option::is_none")]
410 pub has_attachment: Option<bool>,
411 #[serde(skip_serializing_if = "Option::is_none")]
412 pub text: Option<String>,
413 #[serde(skip_serializing_if = "Option::is_none")]
414 pub from: Option<String>,
415 #[serde(skip_serializing_if = "Option::is_none")]
416 pub to: Option<String>,
417 #[serde(skip_serializing_if = "Option::is_none")]
418 pub cc: Option<String>,
419 #[serde(skip_serializing_if = "Option::is_none")]
420 pub bcc: Option<String>,
421 #[serde(skip_serializing_if = "Option::is_none")]
422 pub subject: Option<String>,
423 #[serde(skip_serializing_if = "Option::is_none")]
424 pub body: Option<String>,
425 #[serde(skip_serializing_if = "Option::is_none")]
426 pub header: Option<Vec<String>>,
427}
428
429#[derive(Debug, Clone, Deserialize)]
431#[serde(rename_all = "camelCase")]
432pub struct EmailSort {
433 pub property: String,
434 #[serde(skip_serializing_if = "Option::is_none")]
435 pub is_ascending: Option<bool>,
436 #[serde(skip_serializing_if = "Option::is_none")]
437 pub collation: Option<String>,
438}
439
440#[derive(Debug, Clone, Serialize)]
442#[serde(rename_all = "camelCase")]
443pub struct EmailQueryResponse {
444 pub account_id: String,
445 pub query_state: String,
446 pub can_calculate_changes: bool,
447 pub position: i64,
448 pub ids: Vec<String>,
449 #[serde(skip_serializing_if = "Option::is_none")]
450 pub total: Option<u64>,
451 #[serde(skip_serializing_if = "Option::is_none")]
452 pub limit: Option<u64>,
453}