1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub struct Session {
11 pub capabilities: HashMap<String, Capability>,
13
14 pub accounts: HashMap<String, Account>,
16
17 pub primary_accounts: HashMap<String, String>,
19
20 pub username: String,
22
23 pub api_url: String,
25
26 pub download_url: String,
28
29 pub upload_url: String,
31
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub event_source_url: Option<String>,
35
36 pub state: String,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42#[serde(rename_all = "camelCase")]
43pub struct Capability {
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub max_concurrent_requests: Option<u32>,
47
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub max_calls_in_request: Option<u32>,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub max_size_request: Option<u64>,
55
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub max_size_upload: Option<u64>,
59
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub max_objects_in_get: Option<u32>,
63
64 #[serde(skip_serializing_if = "Option::is_none")]
66 pub max_objects_in_set: Option<u32>,
67
68 #[serde(skip_serializing_if = "Option::is_none")]
70 pub collation_algorithms: Option<Vec<String>>,
71}
72
73impl Default for Capability {
74 fn default() -> Self {
75 Self {
76 max_concurrent_requests: Some(4),
77 max_calls_in_request: Some(16),
78 max_size_request: Some(10 * 1024 * 1024), max_size_upload: Some(50 * 1024 * 1024), max_objects_in_get: Some(500),
81 max_objects_in_set: Some(500),
82 collation_algorithms: Some(vec![
83 "i;ascii-numeric".to_string(),
84 "i;ascii-casemap".to_string(),
85 ]),
86 }
87 }
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
92#[serde(rename_all = "camelCase")]
93pub struct Account {
94 pub name: String,
96
97 pub is_personal: bool,
99
100 pub is_read_only: bool,
102
103 pub account_capabilities: HashMap<String, AccountCapability>,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109#[serde(rename_all = "camelCase")]
110pub struct AccountCapability {
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub max_mailboxes_per_email: Option<u32>,
114
115 #[serde(skip_serializing_if = "Option::is_none")]
117 pub max_mailbox_depth: Option<u32>,
118
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub max_size_mailbox_name: Option<u32>,
122
123 #[serde(skip_serializing_if = "Option::is_none")]
125 pub max_size_attachments_per_email: Option<u64>,
126
127 #[serde(skip_serializing_if = "Option::is_none")]
129 pub email_query_sort_options: Option<Vec<String>>,
130
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub may_upload_script: Option<bool>,
134}
135
136impl Default for AccountCapability {
137 fn default() -> Self {
138 Self {
139 max_mailboxes_per_email: Some(10),
140 max_mailbox_depth: Some(10),
141 max_size_mailbox_name: Some(255),
142 max_size_attachments_per_email: Some(50 * 1024 * 1024), email_query_sort_options: Some(vec![
144 "receivedAt".to_string(),
145 "from".to_string(),
146 "subject".to_string(),
147 "size".to_string(),
148 ]),
149 may_upload_script: Some(false),
150 }
151 }
152}
153
154impl Session {
155 pub fn new(username: String, account_id: String, base_url: String) -> Self {
157 let mut capabilities = HashMap::new();
158 let mut accounts = HashMap::new();
159 let mut primary_accounts = HashMap::new();
160
161 let core_cap_uri = "urn:ietf:params:jmap:core".to_string();
163 capabilities.insert(core_cap_uri.clone(), Capability::default());
164
165 let mail_cap_uri = "urn:ietf:params:jmap:mail".to_string();
167 capabilities.insert(mail_cap_uri.clone(), Capability::default());
168
169 let submission_cap_uri = "urn:ietf:params:jmap:submission".to_string();
171 capabilities.insert(submission_cap_uri.clone(), Capability::default());
172
173 let mut account_caps = HashMap::new();
175 account_caps.insert(mail_cap_uri.clone(), AccountCapability::default());
176 account_caps.insert(submission_cap_uri.clone(), AccountCapability::default());
177
178 let account = Account {
180 name: username.clone(),
181 is_personal: true,
182 is_read_only: false,
183 account_capabilities: account_caps,
184 };
185
186 accounts.insert(account_id.clone(), account);
187
188 primary_accounts.insert(core_cap_uri, account_id.clone());
190 primary_accounts.insert(mail_cap_uri, account_id.clone());
191 primary_accounts.insert(submission_cap_uri, account_id);
192
193 Self {
194 capabilities,
195 accounts,
196 primary_accounts,
197 username,
198 api_url: format!("{}/jmap", base_url),
199 download_url: format!("{}/download/{{accountId}}/{{blobId}}/{{name}}", base_url),
200 upload_url: format!("{}/upload/{{accountId}}", base_url),
201 event_source_url: Some(format!("{}/eventsource", base_url)),
202 state: "session-state-1".to_string(),
203 }
204 }
205}