jmap_client/mailbox/
mod.rs

1/*
2 * Copyright Stalwart Labs LLC See the COPYING
3 * file at the top-level directory of this distribution.
4 *
5 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
8 * option. This file may not be copied, modified, or distributed
9 * except according to those terms.
10 */
11
12pub mod get;
13pub mod helpers;
14pub mod query;
15pub mod set;
16
17use crate::core::changes::ChangesObject;
18use crate::core::set::{map_not_set, string_not_set};
19use crate::core::Object;
20use crate::mailbox::set::role_not_set;
21use crate::principal::ACL;
22use crate::{Get, Set};
23use ahash::AHashMap;
24use serde::{Deserialize, Serialize};
25use std::fmt::Display;
26
27#[derive(Debug, Clone, Serialize, Default)]
28pub struct SetArguments {
29    #[serde(rename = "onDestroyRemoveEmails")]
30    #[serde(skip_serializing_if = "Option::is_none")]
31    on_destroy_remove_emails: Option<bool>,
32}
33
34#[derive(Debug, Clone, Serialize, Default)]
35pub struct QueryArguments {
36    #[serde(rename = "sortAsTree")]
37    sort_as_tree: bool,
38    #[serde(rename = "filterAsTree")]
39    filter_as_tree: bool,
40}
41
42#[derive(Debug, Deserialize, Default)]
43pub struct ChangesResponse {
44    #[serde(rename = "updatedProperties")]
45    updated_properties: Option<Vec<Property>>,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct Mailbox<State = Get> {
50    #[serde(skip)]
51    _create_id: Option<usize>,
52
53    #[serde(skip)]
54    _state: std::marker::PhantomData<State>,
55
56    #[serde(rename = "id")]
57    #[serde(skip_serializing_if = "Option::is_none")]
58    id: Option<String>,
59
60    #[serde(rename = "name")]
61    #[serde(skip_serializing_if = "Option::is_none")]
62    name: Option<String>,
63
64    #[serde(rename = "parentId")]
65    #[serde(skip_serializing_if = "string_not_set")]
66    parent_id: Option<String>,
67
68    #[serde(rename = "role")]
69    #[serde(skip_serializing_if = "role_not_set")]
70    role: Option<Role>,
71
72    #[serde(rename = "sortOrder")]
73    #[serde(skip_serializing_if = "Option::is_none")]
74    sort_order: Option<u32>,
75
76    #[serde(rename = "totalEmails")]
77    #[serde(skip_serializing_if = "Option::is_none")]
78    total_emails: Option<usize>,
79
80    #[serde(rename = "unreadEmails")]
81    #[serde(skip_serializing_if = "Option::is_none")]
82    unread_emails: Option<usize>,
83
84    #[serde(rename = "totalThreads")]
85    #[serde(skip_serializing_if = "Option::is_none")]
86    total_threads: Option<usize>,
87
88    #[serde(rename = "unreadThreads")]
89    #[serde(skip_serializing_if = "Option::is_none")]
90    unread_threads: Option<usize>,
91
92    #[serde(rename = "myRights")]
93    #[serde(skip_serializing_if = "Option::is_none")]
94    my_rights: Option<MailboxRights>,
95
96    #[serde(rename = "isSubscribed")]
97    #[serde(skip_serializing_if = "Option::is_none")]
98    is_subscribed: Option<bool>,
99
100    #[serde(rename = "shareWith")]
101    #[serde(skip_serializing_if = "map_not_set")]
102    share_with: Option<AHashMap<String, AHashMap<ACL, bool>>>,
103
104    #[serde(flatten)]
105    #[serde(skip_deserializing)]
106    #[serde(skip_serializing_if = "Option::is_none")]
107    acl_patch: Option<AHashMap<String, ACLPatch>>,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
111#[serde(untagged)]
112pub(crate) enum ACLPatch {
113    Replace(AHashMap<ACL, bool>),
114    Set(bool),
115}
116
117#[derive(Debug, Clone, PartialEq, Eq, Default)]
118pub enum Role {
119    Archive,
120    Drafts,
121    Important,
122    Inbox,
123    Junk,
124    Sent,
125    Trash,
126    Other(String),
127    #[default]
128    None,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
132pub struct MailboxRights {
133    #[serde(rename = "mayReadItems")]
134    #[serde(default)]
135    may_read_items: bool,
136
137    #[serde(rename = "mayAddItems")]
138    #[serde(default)]
139    may_add_items: bool,
140
141    #[serde(rename = "mayRemoveItems")]
142    #[serde(default)]
143    may_remove_items: bool,
144
145    #[serde(rename = "maySetSeen")]
146    #[serde(default)]
147    may_set_seen: bool,
148
149    #[serde(rename = "maySetKeywords")]
150    #[serde(default)]
151    may_set_keywords: bool,
152
153    #[serde(rename = "mayCreateChild")]
154    #[serde(default)]
155    may_create_child: bool,
156
157    #[serde(rename = "mayRename")]
158    #[serde(default)]
159    may_rename: bool,
160
161    #[serde(rename = "mayDelete")]
162    #[serde(default)]
163    may_delete: bool,
164
165    #[serde(rename = "maySubmit")]
166    #[serde(default)]
167    may_submit: bool,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Copy)]
171pub enum Property {
172    #[serde(rename = "id")]
173    Id,
174    #[serde(rename = "name")]
175    Name,
176    #[serde(rename = "parentId")]
177    ParentId,
178    #[serde(rename = "role")]
179    Role,
180    #[serde(rename = "sortOrder")]
181    SortOrder,
182    #[serde(rename = "totalEmails")]
183    TotalEmails,
184    #[serde(rename = "unreadEmails")]
185    UnreadEmails,
186    #[serde(rename = "totalThreads")]
187    TotalThreads,
188    #[serde(rename = "unreadThreads")]
189    UnreadThreads,
190    #[serde(rename = "myRights")]
191    MyRights,
192    #[serde(rename = "isSubscribed")]
193    IsSubscribed,
194    #[serde(rename = "shareWith")]
195    ShareWith,
196}
197
198impl Property {
199    pub fn is_count(&self) -> bool {
200        matches!(
201            self,
202            Property::TotalEmails
203                | Property::UnreadEmails
204                | Property::TotalThreads
205                | Property::UnreadThreads
206        )
207    }
208}
209
210impl Display for Property {
211    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
212        match self {
213            Property::Id => write!(f, "id"),
214            Property::Name => write!(f, "name"),
215            Property::ParentId => write!(f, "parentId"),
216            Property::Role => write!(f, "role"),
217            Property::SortOrder => write!(f, "sortOrder"),
218            Property::TotalEmails => write!(f, "totalEmails"),
219            Property::UnreadEmails => write!(f, "unreadEmails"),
220            Property::TotalThreads => write!(f, "totalThreads"),
221            Property::UnreadThreads => write!(f, "unreadThreads"),
222            Property::MyRights => write!(f, "myRights"),
223            Property::IsSubscribed => write!(f, "isSubscribed"),
224            Property::ShareWith => write!(f, "shareWith"),
225        }
226    }
227}
228
229impl ChangesResponse {
230    pub fn updated_properties(&self) -> Option<&[Property]> {
231        self.updated_properties.as_deref()
232    }
233}
234
235impl Object for Mailbox<Set> {
236    type Property = Property;
237
238    fn requires_account_id() -> bool {
239        true
240    }
241}
242
243impl Object for Mailbox<Get> {
244    type Property = Property;
245
246    fn requires_account_id() -> bool {
247        true
248    }
249}
250
251impl ChangesObject for Mailbox<Set> {
252    type ChangesResponse = ChangesResponse;
253}
254
255impl ChangesObject for Mailbox<Get> {
256    type ChangesResponse = ChangesResponse;
257}
258
259impl<'de> Deserialize<'de> for Role {
260    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
261    where
262        D: serde::Deserializer<'de>,
263    {
264        match <&str>::deserialize(deserializer)?
265            .to_ascii_lowercase()
266            .as_str()
267        {
268            "inbox" => Ok(Role::Inbox),
269            "sent" => Ok(Role::Sent),
270            "trash" => Ok(Role::Trash),
271            "drafts" => Ok(Role::Drafts),
272            "junk" => Ok(Role::Junk),
273            "archive" => Ok(Role::Archive),
274            "important" => Ok(Role::Important),
275            other => Ok(Role::Other(other.to_string())),
276        }
277    }
278}
279
280impl Serialize for Role {
281    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
282    where
283        S: serde::Serializer,
284    {
285        serializer.serialize_str(match self {
286            Role::Inbox => "inbox",
287            Role::Sent => "sent",
288            Role::Trash => "trash",
289            Role::Drafts => "drafts",
290            Role::Junk => "junk",
291            Role::Archive => "archive",
292            Role::Important => "important",
293            Role::Other(other) => other,
294            Role::None => "",
295        })
296    }
297}