1pub mod get;
13pub mod helpers;
14pub mod import;
15pub mod parse;
16pub mod query;
17pub mod search_snippet;
18pub mod set;
19use ahash::AHashMap;
20use chrono::{DateTime, Utc};
21use serde::{de::Visitor, Deserialize, Serialize};
22use std::fmt::{self, Display, Formatter};
23
24use crate::{
25 core::{changes::ChangesObject, request::ResultReference, Object},
26 Get, Set,
27};
28
29impl Object for Email<Set> {
30 type Property = Property;
31
32 fn requires_account_id() -> bool {
33 true
34 }
35}
36
37impl Object for Email<Get> {
38 type Property = Property;
39
40 fn requires_account_id() -> bool {
41 true
42 }
43}
44
45impl ChangesObject for Email<Set> {
46 type ChangesResponse = ();
47}
48
49impl ChangesObject for Email<Get> {
50 type ChangesResponse = ();
51}
52
53#[derive(Debug, Default, Clone, Serialize, Deserialize)]
54pub struct Email<State = Get> {
55 #[serde(skip)]
56 _create_id: Option<usize>,
57
58 #[serde(skip)]
59 _state: std::marker::PhantomData<State>,
60
61 #[serde(rename = "id")]
62 #[serde(skip_serializing_if = "Option::is_none")]
63 id: Option<String>,
64
65 #[serde(rename = "blobId")]
66 #[serde(skip_serializing_if = "Option::is_none")]
67 blob_id: Option<String>,
68
69 #[serde(rename = "threadId")]
70 #[serde(skip_serializing_if = "Option::is_none")]
71 thread_id: Option<String>,
72
73 #[serde(rename = "mailboxIds")]
74 #[serde(skip_serializing_if = "Option::is_none")]
75 mailbox_ids: Option<AHashMap<String, bool>>,
76
77 #[serde(rename = "#mailboxIds")]
78 #[serde(skip_deserializing)]
79 #[serde(skip_serializing_if = "Option::is_none")]
80 mailbox_ids_ref: Option<ResultReference>,
81
82 #[serde(rename = "keywords")]
83 #[serde(skip_serializing_if = "Option::is_none")]
84 keywords: Option<AHashMap<String, bool>>,
85
86 #[serde(rename = "size")]
87 #[serde(skip_serializing_if = "Option::is_none")]
88 size: Option<usize>,
89
90 #[serde(rename = "receivedAt")]
91 #[serde(skip_serializing_if = "Option::is_none")]
92 received_at: Option<DateTime<Utc>>,
93
94 #[cfg_attr(
95 not(feature = "debug"),
96 serde(alias = "header:Message-ID:asMessageIds")
97 )]
98 #[serde(rename = "messageId")]
99 #[serde(skip_serializing_if = "Option::is_none")]
100 message_id: Option<Vec<String>>,
101
102 #[serde(rename = "inReplyTo")]
103 #[cfg_attr(
104 not(feature = "debug"),
105 serde(alias = "header:In-Reply-To:asMessageIds")
106 )]
107 #[serde(skip_serializing_if = "Option::is_none")]
108 in_reply_to: Option<Vec<String>>,
109
110 #[serde(rename = "references")]
111 #[cfg_attr(
112 not(feature = "debug"),
113 serde(alias = "header:References:asMessageIds")
114 )]
115 #[serde(skip_serializing_if = "Option::is_none")]
116 references: Option<Vec<String>>,
117
118 #[serde(rename = "sender")]
119 #[cfg_attr(not(feature = "debug"), serde(alias = "header:Sender:asAddresses"))]
120 #[serde(skip_serializing_if = "Option::is_none")]
121 sender: Option<Vec<EmailAddress>>,
122
123 #[serde(rename = "from")]
124 #[cfg_attr(not(feature = "debug"), serde(alias = "header:From:asAddresses"))]
125 #[serde(skip_serializing_if = "Option::is_none")]
126 from: Option<Vec<EmailAddress>>,
127
128 #[serde(rename = "to")]
129 #[cfg_attr(not(feature = "debug"), serde(alias = "header:To:asAddresses"))]
130 #[serde(skip_serializing_if = "Option::is_none")]
131 to: Option<Vec<EmailAddress>>,
132
133 #[serde(rename = "cc")]
134 #[cfg_attr(not(feature = "debug"), serde(alias = "header:Cc:asAddresses"))]
135 #[serde(skip_serializing_if = "Option::is_none")]
136 cc: Option<Vec<EmailAddress>>,
137
138 #[serde(rename = "bcc")]
139 #[cfg_attr(not(feature = "debug"), serde(alias = "header:Bcc:asAddresses"))]
140 #[serde(skip_serializing_if = "Option::is_none")]
141 bcc: Option<Vec<EmailAddress>>,
142
143 #[serde(rename = "replyTo")]
144 #[cfg_attr(not(feature = "debug"), serde(alias = "header:Reply-To:asAddresses"))]
145 #[serde(skip_serializing_if = "Option::is_none")]
146 reply_to: Option<Vec<EmailAddress>>,
147
148 #[serde(rename = "subject")]
149 #[cfg_attr(not(feature = "debug"), serde(alias = "header:Subject:asText"))]
150 #[serde(skip_serializing_if = "Option::is_none")]
151 subject: Option<String>,
152
153 #[serde(rename = "sentAt")]
154 #[cfg_attr(not(feature = "debug"), serde(alias = "header:Date:asDate"))]
155 #[serde(skip_serializing_if = "Option::is_none")]
156 sent_at: Option<DateTime<Utc>>,
157
158 #[serde(rename = "bodyStructure")]
159 #[serde(skip_serializing_if = "Option::is_none")]
160 body_structure: Option<Box<EmailBodyPart>>,
161
162 #[serde(rename = "bodyValues")]
163 #[serde(skip_serializing_if = "Option::is_none")]
164 body_values: Option<AHashMap<String, EmailBodyValue>>,
165
166 #[serde(rename = "textBody")]
167 #[serde(skip_serializing_if = "Option::is_none")]
168 text_body: Option<Vec<EmailBodyPart>>,
169
170 #[serde(rename = "htmlBody")]
171 #[serde(skip_serializing_if = "Option::is_none")]
172 html_body: Option<Vec<EmailBodyPart>>,
173
174 #[serde(rename = "attachments")]
175 #[serde(skip_serializing_if = "Option::is_none")]
176 attachments: Option<Vec<EmailBodyPart>>,
177
178 #[serde(rename = "hasAttachment")]
179 #[serde(skip_serializing_if = "Option::is_none")]
180 has_attachment: Option<bool>,
181
182 #[serde(rename = "preview")]
183 #[serde(skip_serializing_if = "Option::is_none")]
184 preview: Option<String>,
185
186 #[serde(flatten)]
187 #[serde(skip_serializing_if = "std::collections::HashMap::is_empty")]
188 headers: AHashMap<Header, Option<HeaderValue>>,
189
190 #[serde(flatten)]
191 #[serde(skip_deserializing)]
192 #[serde(skip_serializing_if = "Option::is_none")]
193 patch: Option<AHashMap<String, bool>>,
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct EmailBodyPart<State = Get> {
198 #[serde(skip)]
199 _state: std::marker::PhantomData<State>,
200
201 #[serde(rename = "partId")]
202 #[serde(skip_serializing_if = "Option::is_none")]
203 part_id: Option<String>,
204
205 #[serde(rename = "blobId")]
206 #[serde(skip_serializing_if = "Option::is_none")]
207 blob_id: Option<String>,
208
209 #[serde(rename = "size")]
210 #[serde(skip_serializing_if = "Option::is_none")]
211 size: Option<usize>,
212
213 #[serde(rename = "headers")]
214 #[serde(skip_serializing_if = "Option::is_none")]
215 headers: Option<Vec<EmailHeader>>,
216
217 #[serde(rename = "name")]
218 #[serde(skip_serializing_if = "Option::is_none")]
219 name: Option<String>,
220
221 #[serde(rename = "type")]
222 #[serde(skip_serializing_if = "Option::is_none")]
223 type_: Option<String>,
224
225 #[serde(rename = "charset")]
226 #[serde(skip_serializing_if = "Option::is_none")]
227 charset: Option<String>,
228
229 #[serde(rename = "disposition")]
230 #[serde(skip_serializing_if = "Option::is_none")]
231 disposition: Option<String>,
232
233 #[serde(rename = "cid")]
234 #[serde(skip_serializing_if = "Option::is_none")]
235 cid: Option<String>,
236
237 #[serde(rename = "language")]
238 #[serde(skip_serializing_if = "Option::is_none")]
239 language: Option<Vec<String>>,
240
241 #[serde(rename = "location")]
242 #[serde(skip_serializing_if = "Option::is_none")]
243 location: Option<String>,
244
245 #[serde(rename = "subParts")]
246 #[serde(skip_serializing_if = "Option::is_none")]
247 sub_parts: Option<Vec<EmailBodyPart>>,
248
249 #[serde(flatten)]
250 #[serde(skip_serializing_if = "Option::is_none")]
251 header: Option<AHashMap<Header, HeaderValue>>,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
255pub struct EmailBodyValue<State = Get> {
256 #[serde(skip)]
257 _state: std::marker::PhantomData<State>,
258
259 #[serde(rename = "value")]
260 value: String,
261
262 #[serde(rename = "isEncodingProblem")]
263 #[serde(skip_serializing_if = "Option::is_none")]
264 is_encoding_problem: Option<bool>,
265
266 #[serde(rename = "isTruncated")]
267 #[serde(skip_serializing_if = "Option::is_none")]
268 is_truncated: Option<bool>,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct EmailAddress<State = Get> {
273 #[serde(skip)]
274 _state: std::marker::PhantomData<State>,
275
276 name: Option<String>,
277 email: String,
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct EmailAddressGroup<State = Get> {
282 #[serde(skip)]
283 _state: std::marker::PhantomData<State>,
284
285 name: Option<String>,
286 addresses: Vec<EmailAddress>,
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct EmailHeader<State = Get> {
291 #[serde(skip)]
292 _state: std::marker::PhantomData<State>,
293
294 name: String,
295 value: String,
296}
297
298#[derive(Debug, Clone, PartialEq, Eq, Hash)]
299pub enum Property {
300 Id,
301 BlobId,
302 ThreadId,
303 MailboxIds,
304 Keywords,
305 Size,
306 ReceivedAt,
307 MessageId,
308 InReplyTo,
309 References,
310 Sender,
311 From,
312 To,
313 Cc,
314 Bcc,
315 ReplyTo,
316 Subject,
317 SentAt,
318 BodyStructure,
319 BodyValues,
320 TextBody,
321 HtmlBody,
322 Attachments,
323 HasAttachment,
324 Preview,
325 Header(Header),
326 Other(String),
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
330#[serde(untagged)]
331pub enum HeaderValue {
332 AsDate(DateTime<Utc>),
333 AsDateAll(Vec<DateTime<Utc>>),
334 AsText(String),
335 AsTextAll(Vec<String>),
336 AsTextListAll(Vec<Vec<String>>),
337 AsAddressesAll(Vec<Vec<EmailAddress>>),
338 AsAddresses(Vec<EmailAddress>),
339 AsGroupedAddressesAll(Vec<Vec<EmailAddressGroup>>),
340 AsGroupedAddresses(Vec<EmailAddressGroup>),
341}
342
343#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone)]
344pub struct Header {
345 pub name: String,
346 pub form: HeaderForm,
347 pub all: bool,
348}
349
350#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)]
351pub enum HeaderForm {
352 Raw,
353 Text,
354 Addresses,
355 GroupedAddresses,
356 MessageIds,
357 Date,
358 URLs,
359}
360
361impl Property {
362 fn parse(value: &str) -> Option<Self> {
363 match value {
364 "id" => Some(Property::Id),
365 "blobId" => Some(Property::BlobId),
366 "threadId" => Some(Property::ThreadId),
367 "mailboxIds" => Some(Property::MailboxIds),
368 "keywords" => Some(Property::Keywords),
369 "size" => Some(Property::Size),
370 "receivedAt" => Some(Property::ReceivedAt),
371 "messageId" => Some(Property::MessageId),
372 "inReplyTo" => Some(Property::InReplyTo),
373 "references" => Some(Property::References),
374 "sender" => Some(Property::Sender),
375 "from" => Some(Property::From),
376 "to" => Some(Property::To),
377 "cc" => Some(Property::Cc),
378 "bcc" => Some(Property::Bcc),
379 "replyTo" => Some(Property::ReplyTo),
380 "subject" => Some(Property::Subject),
381 "sentAt" => Some(Property::SentAt),
382 "hasAttachment" => Some(Property::HasAttachment),
383 "preview" => Some(Property::Preview),
384 "bodyValues" => Some(Property::BodyValues),
385 "textBody" => Some(Property::TextBody),
386 "htmlBody" => Some(Property::HtmlBody),
387 "attachments" => Some(Property::Attachments),
388 "bodyStructure" => Some(Property::BodyStructure),
389 _ if value.starts_with("header:") => Some(Property::Header(Header::parse(value)?)),
390 _ => Some(Property::Other(value.to_string())),
391 }
392 }
393}
394
395impl Display for Property {
396 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
397 match self {
398 Property::Id => write!(f, "id"),
399 Property::BlobId => write!(f, "blobId"),
400 Property::ThreadId => write!(f, "threadId"),
401 Property::MailboxIds => write!(f, "mailboxIds"),
402 Property::Keywords => write!(f, "keywords"),
403 Property::Size => write!(f, "size"),
404 Property::ReceivedAt => write!(f, "receivedAt"),
405 Property::MessageId => write!(f, "messageId"),
406 Property::InReplyTo => write!(f, "inReplyTo"),
407 Property::References => write!(f, "references"),
408 Property::Sender => write!(f, "sender"),
409 Property::From => write!(f, "from"),
410 Property::To => write!(f, "to"),
411 Property::Cc => write!(f, "cc"),
412 Property::Bcc => write!(f, "bcc"),
413 Property::ReplyTo => write!(f, "replyTo"),
414 Property::Subject => write!(f, "subject"),
415 Property::SentAt => write!(f, "sentAt"),
416 Property::BodyStructure => write!(f, "bodyStructure"),
417 Property::BodyValues => write!(f, "bodyValues"),
418 Property::TextBody => write!(f, "textBody"),
419 Property::HtmlBody => write!(f, "htmlBody"),
420 Property::Attachments => write!(f, "attachments"),
421 Property::HasAttachment => write!(f, "hasAttachment"),
422 Property::Preview => write!(f, "preview"),
423 Property::Header(header) => header.fmt(f),
424 Property::Other(other) => write!(f, "{}", other),
425 }
426 }
427}
428
429impl Serialize for Property {
430 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
431 where
432 S: serde::Serializer,
433 {
434 serializer.serialize_str(&self.to_string())
435 }
436}
437
438struct PropertyVisitor;
439
440impl<'de> Visitor<'de> for PropertyVisitor {
441 type Value = Property;
442
443 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
444 formatter.write_str("a valid JMAP e-mail property")
445 }
446
447 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
448 where
449 E: serde::de::Error,
450 {
451 Property::parse(v).ok_or_else(|| {
452 serde::de::Error::custom(format!("Failed to parse JMAP property '{}'", v))
453 })
454 }
455}
456
457impl<'de> Deserialize<'de> for Property {
458 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
459 where
460 D: serde::Deserializer<'de>,
461 {
462 deserializer.deserialize_str(PropertyVisitor)
463 }
464}
465
466impl Serialize for Header {
467 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
468 where
469 S: serde::Serializer,
470 {
471 serializer.serialize_str(&self.to_string())
472 }
473}
474
475struct HeaderVisitor;
476
477impl<'de> Visitor<'de> for HeaderVisitor {
478 type Value = Header;
479
480 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
481 formatter.write_str("a valid JMAP header")
482 }
483
484 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
485 where
486 E: serde::de::Error,
487 {
488 Header::parse(v)
489 .ok_or_else(|| serde::de::Error::custom(format!("Failed to parse JMAP header '{}'", v)))
490 }
491}
492
493impl<'de> Deserialize<'de> for Header {
494 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
495 where
496 D: serde::Deserializer<'de>,
497 {
498 deserializer.deserialize_str(HeaderVisitor)
499 }
500}
501
502impl HeaderForm {
503 pub fn parse(value: &str) -> Option<HeaderForm> {
504 match value {
505 "asText" => Some(HeaderForm::Text),
506 "asAddresses" => Some(HeaderForm::Addresses),
507 "asGroupedAddresses" => Some(HeaderForm::GroupedAddresses),
508 "asMessageIds" => Some(HeaderForm::MessageIds),
509 "asDate" => Some(HeaderForm::Date),
510 "asURLs" => Some(HeaderForm::URLs),
511 _ => None,
512 }
513 }
514}
515
516impl Header {
517 pub fn as_raw(name: impl Into<String>, all: bool) -> Header {
518 Header {
519 name: name.into(),
520 form: HeaderForm::Raw,
521 all,
522 }
523 }
524
525 pub fn as_text(name: impl Into<String>, all: bool) -> Header {
526 Header {
527 name: name.into(),
528 form: HeaderForm::Text,
529 all,
530 }
531 }
532
533 pub fn as_addresses(name: impl Into<String>, all: bool) -> Header {
534 Header {
535 name: name.into(),
536 form: HeaderForm::Addresses,
537 all,
538 }
539 }
540
541 pub fn as_grouped_addresses(name: impl Into<String>, all: bool) -> Header {
542 Header {
543 name: name.into(),
544 form: HeaderForm::GroupedAddresses,
545 all,
546 }
547 }
548
549 pub fn as_message_ids(name: impl Into<String>, all: bool) -> Header {
550 Header {
551 name: name.into(),
552 form: HeaderForm::MessageIds,
553 all,
554 }
555 }
556
557 pub fn as_date(name: impl Into<String>, all: bool) -> Header {
558 Header {
559 name: name.into(),
560 form: HeaderForm::Date,
561 all,
562 }
563 }
564
565 pub fn as_urls(name: impl Into<String>, all: bool) -> Header {
566 Header {
567 name: name.into(),
568 form: HeaderForm::URLs,
569 all,
570 }
571 }
572
573 pub fn parse(value: &str) -> Option<Header> {
574 let mut all = false;
575 let mut form = HeaderForm::Raw;
576 let mut header = None;
577 for (pos, part) in value.split(':').enumerate() {
578 match pos {
579 0 if part == "header" => (),
580 1 => {
581 header = part.into();
582 }
583 2 | 3 if part == "all" => all = true,
584 2 => {
585 form = HeaderForm::parse(part)?;
586 }
587 _ => return None,
588 }
589 }
590 Header {
591 name: header?.to_string(),
592 form,
593 all,
594 }
595 .into()
596 }
597}
598
599impl Display for Header {
600 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
601 write!(f, "header:")?;
602 self.name.fmt(f)?;
603 self.form.fmt(f)?;
604 if self.all {
605 write!(f, ":all")
606 } else {
607 Ok(())
608 }
609 }
610}
611
612impl Display for HeaderForm {
613 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
614 match self {
615 HeaderForm::Raw => Ok(()),
616 HeaderForm::Text => write!(f, ":asText"),
617 HeaderForm::Addresses => write!(f, ":asAddresses"),
618 HeaderForm::GroupedAddresses => write!(f, ":asGroupedAddresses"),
619 HeaderForm::MessageIds => write!(f, ":asMessageIds"),
620 HeaderForm::Date => write!(f, ":asDate"),
621 HeaderForm::URLs => write!(f, ":asURLs"),
622 }
623 }
624}
625
626#[derive(Debug, Clone, PartialEq, Eq, Hash)]
627pub enum BodyProperty {
628 PartId,
629 BlobId,
630 Size,
631 Headers,
632 Name,
633 Type,
634 Charset,
635 Disposition,
636 Cid,
637 Language,
638 Location,
639 SubParts,
640 Header(Header),
641}
642
643impl BodyProperty {
644 fn parse(value: &str) -> Option<BodyProperty> {
645 match value {
646 "partId" => Some(BodyProperty::PartId),
647 "blobId" => Some(BodyProperty::BlobId),
648 "size" => Some(BodyProperty::Size),
649 "name" => Some(BodyProperty::Name),
650 "type" => Some(BodyProperty::Type),
651 "charset" => Some(BodyProperty::Charset),
652 "headers" => Some(BodyProperty::Headers),
653 "disposition" => Some(BodyProperty::Disposition),
654 "cid" => Some(BodyProperty::Cid),
655 "language" => Some(BodyProperty::Language),
656 "location" => Some(BodyProperty::Location),
657 "subParts" => Some(BodyProperty::SubParts),
658 _ if value.starts_with("header:") => Some(BodyProperty::Header(Header::parse(value)?)),
659 _ => None,
660 }
661 }
662}
663
664impl Display for BodyProperty {
665 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
666 match self {
667 BodyProperty::PartId => write!(f, "partId"),
668 BodyProperty::BlobId => write!(f, "blobId"),
669 BodyProperty::Size => write!(f, "size"),
670 BodyProperty::Name => write!(f, "name"),
671 BodyProperty::Type => write!(f, "type"),
672 BodyProperty::Charset => write!(f, "charset"),
673 BodyProperty::Header(header) => header.fmt(f),
674 BodyProperty::Headers => write!(f, "headers"),
675 BodyProperty::Disposition => write!(f, "disposition"),
676 BodyProperty::Cid => write!(f, "cid"),
677 BodyProperty::Language => write!(f, "language"),
678 BodyProperty::Location => write!(f, "location"),
679 BodyProperty::SubParts => write!(f, "subParts"),
680 }
681 }
682}
683
684impl Serialize for BodyProperty {
685 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
686 where
687 S: serde::Serializer,
688 {
689 serializer.serialize_str(&self.to_string())
690 }
691}
692
693struct BodyPropertyVisitor;
694
695impl<'de> Visitor<'de> for BodyPropertyVisitor {
696 type Value = BodyProperty;
697
698 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
699 formatter.write_str("a valid JMAP body property")
700 }
701
702 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
703 where
704 E: serde::de::Error,
705 {
706 BodyProperty::parse(v).ok_or_else(|| {
707 serde::de::Error::custom(format!("Failed to parse JMAP body property '{}'", v))
708 })
709 }
710}
711
712impl<'de> Deserialize<'de> for BodyProperty {
713 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
714 where
715 D: serde::Deserializer<'de>,
716 {
717 deserializer.deserialize_str(BodyPropertyVisitor)
718 }
719}
720
721#[derive(Debug, Clone, Serialize, Deserialize)]
722pub struct MailCapabilities {
723 #[serde(rename = "maxMailboxesPerEmail")]
724 max_mailboxes_per_email: Option<usize>,
725
726 #[serde(rename = "maxMailboxDepth")]
727 max_mailbox_depth: usize,
728
729 #[serde(rename = "maxSizeMailboxName")]
730 max_size_mailbox_name: usize,
731
732 #[serde(rename = "maxSizeAttachmentsPerEmail")]
733 max_size_attachments_per_email: usize,
734
735 #[serde(rename = "emailQuerySortOptions")]
736 email_query_sort_options: Vec<String>,
737
738 #[serde(rename = "mayCreateTopLevelMailbox")]
739 may_create_top_level_mailbox: bool,
740}
741
742#[derive(Debug, Clone, Serialize, Deserialize)]
743pub struct SubmissionCapabilities {
744 #[serde(rename = "maxDelayedSend")]
745 max_delayed_send: usize,
746
747 #[serde(rename = "submissionExtensions")]
748 submission_extensions: Vec<String>,
749}
750
751#[derive(Debug, Clone, Serialize, Default)]
752pub struct QueryArguments {
753 #[serde(rename = "collapseThreads")]
754 #[serde(skip_serializing_if = "Option::is_none")]
755 collapse_threads: Option<bool>,
756}
757
758#[derive(Debug, Clone, Serialize, Default)]
759pub struct GetArguments {
760 #[serde(rename = "bodyProperties")]
761 #[serde(skip_serializing_if = "Option::is_none")]
762 body_properties: Option<Vec<BodyProperty>>,
763
764 #[serde(rename = "fetchTextBodyValues")]
765 #[serde(skip_serializing_if = "Option::is_none")]
766 fetch_text_body_values: Option<bool>,
767
768 #[serde(rename = "fetchHTMLBodyValues")]
769 #[serde(skip_serializing_if = "Option::is_none")]
770 fetch_html_body_values: Option<bool>,
771
772 #[serde(rename = "fetchAllBodyValues")]
773 #[serde(skip_serializing_if = "Option::is_none")]
774 fetch_all_body_values: Option<bool>,
775
776 #[serde(rename = "maxBodyValueBytes")]
777 #[serde(skip_serializing_if = "Option::is_none")]
778 max_body_value_bytes: Option<usize>,
779}
780
781impl QueryArguments {
782 pub fn collapse_threads(&mut self, collapse_threads: bool) {
783 self.collapse_threads = collapse_threads.into();
784 }
785}
786
787impl GetArguments {
788 pub fn body_properties(
789 &mut self,
790 body_properties: impl IntoIterator<Item = BodyProperty>,
791 ) -> &mut Self {
792 self.body_properties = Some(body_properties.into_iter().collect());
793 self
794 }
795
796 pub fn fetch_text_body_values(&mut self, fetch_text_body_values: bool) -> &mut Self {
797 self.fetch_text_body_values = fetch_text_body_values.into();
798 self
799 }
800
801 pub fn fetch_html_body_values(&mut self, fetch_html_body_values: bool) -> &mut Self {
802 self.fetch_html_body_values = fetch_html_body_values.into();
803 self
804 }
805
806 pub fn fetch_all_body_values(&mut self, fetch_all_body_values: bool) -> &mut Self {
807 self.fetch_all_body_values = fetch_all_body_values.into();
808 self
809 }
810
811 pub fn max_body_value_bytes(&mut self, max_body_value_bytes: usize) -> &mut Self {
812 self.max_body_value_bytes = max_body_value_bytes.into();
813 self
814 }
815}
816
817impl MailCapabilities {
818 pub fn max_mailboxes_per_email(&self) -> Option<usize> {
819 self.max_mailboxes_per_email
820 }
821
822 pub fn max_mailbox_depth(&self) -> usize {
823 self.max_mailbox_depth
824 }
825
826 pub fn max_size_mailbox_name(&self) -> usize {
827 self.max_size_mailbox_name
828 }
829
830 pub fn max_size_attachments_per_email(&self) -> usize {
831 self.max_size_attachments_per_email
832 }
833
834 pub fn email_query_sort_options(&self) -> &[String] {
835 &self.email_query_sort_options
836 }
837
838 pub fn may_create_top_level_mailbox(&self) -> bool {
839 self.may_create_top_level_mailbox
840 }
841}
842
843impl SubmissionCapabilities {
844 pub fn max_delayed_send(&self) -> usize {
845 self.max_delayed_send
846 }
847
848 pub fn submission_extensions(&self) -> &[String] {
849 &self.submission_extensions
850 }
851}
852
853#[cfg(feature = "debug")]
854use std::collections::BTreeMap;
855
856#[cfg(feature = "debug")]
857#[derive(Debug, Default, Clone, Serialize, Deserialize)]
858pub struct TestEmail {
859 #[serde(rename = "mailboxIds")]
860 pub mailbox_ids: Option<BTreeMap<String, bool>>,
861
862 #[serde(skip_serializing_if = "Option::is_none")]
863 pub keywords: Option<BTreeMap<String, bool>>,
864
865 #[serde(skip_serializing_if = "Option::is_none")]
866 pub size: Option<usize>,
867
868 #[serde(rename = "receivedAt")]
869 #[serde(skip_serializing_if = "Option::is_none")]
870 pub received_at: Option<DateTime<Utc>>,
871
872 #[serde(rename = "messageId")]
873 #[serde(skip_serializing_if = "Option::is_none")]
874 pub message_id: Option<Vec<String>>,
875
876 #[serde(rename = "inReplyTo")]
877 #[serde(skip_serializing_if = "Option::is_none")]
878 pub in_reply_to: Option<Vec<String>>,
879
880 #[serde(skip_serializing_if = "Option::is_none")]
881 pub references: Option<Vec<String>>,
882
883 #[serde(skip_serializing_if = "Option::is_none")]
884 pub sender: Option<Vec<EmailAddress>>,
885
886 #[serde(skip_serializing_if = "Option::is_none")]
887 pub from: Option<Vec<EmailAddress>>,
888
889 #[serde(skip_serializing_if = "Option::is_none")]
890 pub to: Option<Vec<EmailAddress>>,
891
892 #[serde(skip_serializing_if = "Option::is_none")]
893 pub cc: Option<Vec<EmailAddress>>,
894
895 #[serde(skip_serializing_if = "Option::is_none")]
896 pub bcc: Option<Vec<EmailAddress>>,
897
898 #[serde(rename = "replyTo")]
899 #[serde(skip_serializing_if = "Option::is_none")]
900 pub reply_to: Option<Vec<EmailAddress>>,
901
902 #[serde(skip_serializing_if = "Option::is_none")]
903 pub subject: Option<String>,
904
905 #[serde(rename = "sentAt")]
906 #[serde(skip_serializing_if = "Option::is_none")]
907 pub sent_at: Option<DateTime<Utc>>,
908
909 #[serde(rename = "bodyStructure")]
910 #[serde(skip_serializing_if = "Option::is_none")]
911 pub body_structure: Option<Box<EmailBodyPart>>,
912
913 #[serde(rename = "bodyValues")]
914 #[serde(skip_serializing_if = "Option::is_none")]
915 pub body_values: Option<BTreeMap<String, EmailBodyValue>>,
916
917 #[serde(rename = "textBody")]
918 #[serde(skip_serializing_if = "Option::is_none")]
919 pub text_body: Option<Vec<EmailBodyPart>>,
920
921 #[serde(rename = "htmlBody")]
922 #[serde(skip_serializing_if = "Option::is_none")]
923 pub html_body: Option<Vec<EmailBodyPart>>,
924
925 #[serde(skip_serializing_if = "Option::is_none")]
926 pub attachments: Option<Vec<EmailBodyPart>>,
927
928 #[serde(rename = "hasAttachment")]
929 #[serde(skip_serializing_if = "Option::is_none")]
930 pub has_attachment: Option<bool>,
931
932 #[serde(skip_serializing_if = "Option::is_none")]
933 pub preview: Option<String>,
934
935 #[serde(flatten)]
936 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
937 pub headers: BTreeMap<Header, Option<HeaderValue>>,
938}
939
940#[cfg(feature = "debug")]
941impl From<Email> for TestEmail {
942 fn from(email: Email) -> Self {
943 TestEmail {
944 mailbox_ids: email.mailbox_ids.map(|ids| ids.into_iter().collect()),
945 keywords: email
946 .keywords
947 .map(|keywords| keywords.into_iter().collect()),
948 size: email.size,
949 received_at: email.received_at,
950 message_id: email.message_id,
951 in_reply_to: email.in_reply_to,
952 references: email.references,
953 sender: email.sender,
954 from: email.from,
955 to: email.to,
956 cc: email.cc,
957 bcc: email.bcc,
958 reply_to: email.reply_to,
959 subject: email.subject,
960 sent_at: email.sent_at,
961 body_structure: email.body_structure,
962 body_values: email
963 .body_values
964 .map(|body_values| body_values.into_iter().collect()),
965 text_body: email.text_body,
966 html_body: email.html_body,
967 attachments: email.attachments,
968 has_attachment: email.has_attachment,
969 preview: email.preview,
970 headers: email.headers.into_iter().collect(),
971 }
972 }
973}