jmap-client/src/email/mod.rs

479 lines
14 KiB
Rust

pub mod get;
pub mod helpers;
pub mod import;
pub mod parse;
pub mod query;
pub mod search_snippet;
pub mod set;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
fmt::{self, Display, Formatter},
};
use crate::{core::request::ResultReference, Get};
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Email<State = Get> {
#[serde(skip)]
_create_id: Option<usize>,
#[serde(skip)]
_state: std::marker::PhantomData<State>,
#[serde(rename = "id")]
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(rename = "blobId")]
#[serde(skip_serializing_if = "Option::is_none")]
blob_id: Option<String>,
#[serde(rename = "threadId")]
#[serde(skip_serializing_if = "Option::is_none")]
thread_id: Option<String>,
#[serde(rename = "mailboxIds")]
#[serde(skip_serializing_if = "Option::is_none")]
mailbox_ids: Option<HashMap<String, bool>>,
#[serde(rename = "#mailboxIds")]
#[serde(skip_deserializing)]
#[serde(skip_serializing_if = "Option::is_none")]
mailbox_ids_ref: Option<ResultReference>,
#[serde(rename = "keywords")]
#[serde(skip_serializing_if = "Option::is_none")]
keywords: Option<HashMap<String, bool>>,
#[serde(rename = "size")]
#[serde(skip_serializing_if = "Option::is_none")]
size: Option<usize>,
#[serde(rename = "receivedAt")]
#[serde(skip_serializing_if = "Option::is_none")]
received_at: Option<DateTime<Utc>>,
#[serde(rename = "messageId", alias = "header:Message-ID:asMessageIds")]
#[serde(skip_serializing_if = "Option::is_none")]
message_id: Option<Vec<String>>,
#[serde(rename = "inReplyTo", alias = "header:In-Reply-To:asMessageIds")]
#[serde(skip_serializing_if = "Option::is_none")]
in_reply_to: Option<Vec<String>>,
#[serde(rename = "references", alias = "header:References:asMessageIds")]
#[serde(skip_serializing_if = "Option::is_none")]
references: Option<Vec<String>>,
#[serde(rename = "sender", alias = "header:Sender:asAddresses")]
#[serde(skip_serializing_if = "Option::is_none")]
sender: Option<Vec<EmailAddress>>,
#[serde(rename = "from", alias = "header:From:asAddresses")]
#[serde(skip_serializing_if = "Option::is_none")]
from: Option<Vec<EmailAddress>>,
#[serde(rename = "to", alias = "header:To:asAddresses")]
#[serde(skip_serializing_if = "Option::is_none")]
to: Option<Vec<EmailAddress>>,
#[serde(rename = "cc", alias = "header:Cc:asAddresses")]
#[serde(skip_serializing_if = "Option::is_none")]
cc: Option<Vec<EmailAddress>>,
#[serde(rename = "bcc", alias = "header:Bcc:asAddresses")]
#[serde(skip_serializing_if = "Option::is_none")]
bcc: Option<Vec<EmailAddress>>,
#[serde(rename = "replyTo", alias = "header:Reply-To:asAddresses")]
#[serde(skip_serializing_if = "Option::is_none")]
reply_to: Option<Vec<EmailAddress>>,
#[serde(rename = "subject", alias = "header:Subject:asText")]
#[serde(skip_serializing_if = "Option::is_none")]
subject: Option<String>,
#[serde(rename = "sentAt", alias = "header:Date:asDate")]
#[serde(skip_serializing_if = "Option::is_none")]
sent_at: Option<DateTime<Utc>>,
#[serde(rename = "bodyStructure")]
#[serde(skip_serializing_if = "Option::is_none")]
body_structure: Option<Box<EmailBodyPart>>,
#[serde(rename = "bodyValues")]
#[serde(skip_serializing_if = "Option::is_none")]
body_values: Option<HashMap<String, EmailBodyValue>>,
#[serde(rename = "textBody")]
#[serde(skip_serializing_if = "Option::is_none")]
text_body: Option<Vec<EmailBodyPart>>,
#[serde(rename = "htmlBody")]
#[serde(skip_serializing_if = "Option::is_none")]
html_body: Option<Vec<EmailBodyPart>>,
#[serde(rename = "attachments")]
#[serde(skip_serializing_if = "Option::is_none")]
attachments: Option<Vec<EmailBodyPart>>,
#[serde(rename = "hasAttachment")]
#[serde(skip_serializing_if = "Option::is_none")]
has_attachment: Option<bool>,
#[serde(rename = "preview")]
#[serde(skip_serializing_if = "Option::is_none")]
preview: Option<String>,
#[serde(flatten)]
#[serde(skip_serializing_if = "HashMap::is_empty")]
others: HashMap<String, Option<Field>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailBodyPart<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
#[serde(rename = "partId")]
#[serde(skip_serializing_if = "Option::is_none")]
part_id: Option<String>,
#[serde(rename = "blobId")]
#[serde(skip_serializing_if = "Option::is_none")]
blob_id: Option<String>,
#[serde(rename = "size")]
#[serde(skip_serializing_if = "Option::is_none")]
size: Option<usize>,
#[serde(rename = "headers")]
#[serde(skip_serializing_if = "Option::is_none")]
headers: Option<Vec<EmailHeader>>,
#[serde(rename = "name")]
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(rename = "type")]
#[serde(skip_serializing_if = "Option::is_none")]
type_: Option<String>,
#[serde(rename = "charset")]
#[serde(skip_serializing_if = "Option::is_none")]
charset: Option<String>,
#[serde(rename = "disposition")]
#[serde(skip_serializing_if = "Option::is_none")]
disposition: Option<String>,
#[serde(rename = "cid")]
#[serde(skip_serializing_if = "Option::is_none")]
cid: Option<String>,
#[serde(rename = "language")]
#[serde(skip_serializing_if = "Option::is_none")]
language: Option<Vec<String>>,
#[serde(rename = "location")]
#[serde(skip_serializing_if = "Option::is_none")]
location: Option<String>,
#[serde(rename = "subParts")]
#[serde(skip_serializing_if = "Option::is_none")]
sub_parts: Option<Vec<EmailBodyPart>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailBodyValue<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
#[serde(rename = "value")]
value: String,
#[serde(rename = "isEncodingProblem")]
is_encoding_problem: bool,
#[serde(rename = "isTruncated")]
is_truncated: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Field {
Text(String),
TextList(Vec<String>),
Date(DateTime<Utc>),
Addresses(Vec<EmailAddress>),
GroupedAddresses(Vec<EmailAddressGroup>),
Bool(bool),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailAddress<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
name: Option<String>,
email: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailAddressGroup<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
name: Option<String>,
addresses: Vec<EmailAddress>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailHeader<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
name: String,
value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Property {
#[serde(rename = "id")]
Id,
#[serde(rename = "blobId")]
BlobId,
#[serde(rename = "threadId")]
ThreadId,
#[serde(rename = "mailboxIds")]
MailboxIds,
#[serde(rename = "keywords")]
Keywords,
#[serde(rename = "size")]
Size,
#[serde(rename = "receivedAt")]
ReceivedAt,
#[serde(rename = "messageId")]
MessageId,
#[serde(rename = "inReplyTo")]
InReplyTo,
#[serde(rename = "references")]
References,
#[serde(rename = "sender")]
Sender,
#[serde(rename = "from")]
From,
#[serde(rename = "to")]
To,
#[serde(rename = "cc")]
Cc,
#[serde(rename = "bcc")]
Bcc,
#[serde(rename = "replyTo")]
ReplyTo,
#[serde(rename = "subject")]
Subject,
#[serde(rename = "sentAt")]
SentAt,
#[serde(rename = "bodyStructure")]
BodyStructure,
#[serde(rename = "bodyValues")]
BodyValues,
#[serde(rename = "textBody")]
TextBody,
#[serde(rename = "htmlBody")]
HtmlBody,
#[serde(rename = "attachments")]
Attachments,
#[serde(rename = "hasAttachment")]
HasAttachment,
#[serde(rename = "preview")]
Preview,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BodyProperty {
#[serde(rename = "partId")]
PartId,
#[serde(rename = "blobId")]
BlobId,
#[serde(rename = "size")]
Size,
#[serde(rename = "headers")]
Headers,
#[serde(rename = "name")]
Name,
#[serde(rename = "type")]
Type,
#[serde(rename = "charset")]
Charset,
#[serde(rename = "disposition")]
Disposition,
#[serde(rename = "cid")]
Cid,
#[serde(rename = "language")]
Language,
#[serde(rename = "location")]
Location,
#[serde(rename = "subParts")]
SubParts,
}
impl Display for Property {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Property::Id => write!(f, "id"),
Property::BlobId => write!(f, "blobId"),
Property::ThreadId => write!(f, "threadId"),
Property::MailboxIds => write!(f, "mailboxIds"),
Property::Keywords => write!(f, "keywords"),
Property::Size => write!(f, "size"),
Property::ReceivedAt => write!(f, "receivedAt"),
Property::MessageId => write!(f, "messageId"),
Property::InReplyTo => write!(f, "inReplyTo"),
Property::References => write!(f, "references"),
Property::Sender => write!(f, "sender"),
Property::From => write!(f, "from"),
Property::To => write!(f, "to"),
Property::Cc => write!(f, "cc"),
Property::Bcc => write!(f, "bcc"),
Property::ReplyTo => write!(f, "replyTo"),
Property::Subject => write!(f, "subject"),
Property::SentAt => write!(f, "sentAt"),
Property::BodyStructure => write!(f, "bodyStructure"),
Property::BodyValues => write!(f, "bodyValues"),
Property::TextBody => write!(f, "textBody"),
Property::HtmlBody => write!(f, "htmlBody"),
Property::Attachments => write!(f, "attachments"),
Property::HasAttachment => write!(f, "hasAttachment"),
Property::Preview => write!(f, "preview"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MailCapabilities {
#[serde(rename = "maxMailboxesPerEmail")]
max_mailboxes_per_email: Option<usize>,
#[serde(rename = "maxMailboxDepth")]
max_mailbox_depth: usize,
#[serde(rename = "maxSizeMailboxName")]
max_size_mailbox_name: usize,
#[serde(rename = "maxSizeAttachmentsPerEmail")]
max_size_attachments_per_email: usize,
#[serde(rename = "emailQuerySortOptions")]
email_query_sort_options: Vec<String>,
#[serde(rename = "mayCreateTopLevelMailbox")]
may_create_top_level_mailbox: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubmissionCapabilities {
#[serde(rename = "maxDelayedSend")]
max_delayed_send: usize,
#[serde(rename = "submissionExtensions")]
submission_extensions: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct QueryArguments {
#[serde(rename = "collapseThreads")]
collapse_threads: bool,
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct GetArguments {
#[serde(rename = "bodyProperties")]
#[serde(skip_serializing_if = "Option::is_none")]
body_properties: Option<Vec<BodyProperty>>,
#[serde(rename = "fetchTextBodyValues")]
fetch_text_body_values: bool,
#[serde(rename = "fetchHTMLBodyValues")]
fetch_html_body_values: bool,
#[serde(rename = "fetchAllBodyValues")]
fetch_all_body_values: bool,
#[serde(rename = "maxBodyValueBytes")]
max_body_value_bytes: usize,
}
impl QueryArguments {
pub fn collapse_threads(&mut self, collapse_threads: bool) {
self.collapse_threads = collapse_threads;
}
}
impl GetArguments {
pub fn body_properties(
&mut self,
body_properties: impl IntoIterator<Item = BodyProperty>,
) -> &mut Self {
self.body_properties = Some(body_properties.into_iter().collect());
self
}
pub fn fetch_text_body_values(&mut self, fetch_text_body_values: bool) -> &mut Self {
self.fetch_text_body_values = fetch_text_body_values;
self
}
pub fn fetch_html_body_values(&mut self, fetch_html_body_values: bool) -> &mut Self {
self.fetch_html_body_values = fetch_html_body_values;
self
}
pub fn fetch_all_body_values(&mut self, fetch_all_body_values: bool) -> &mut Self {
self.fetch_all_body_values = fetch_all_body_values;
self
}
pub fn max_body_value_bytes(&mut self, max_body_value_bytes: usize) -> &mut Self {
self.max_body_value_bytes = max_body_value_bytes;
self
}
}
impl MailCapabilities {
pub fn max_mailboxes_per_email(&self) -> Option<usize> {
self.max_mailboxes_per_email
}
pub fn max_mailbox_depth(&self) -> usize {
self.max_mailbox_depth
}
pub fn max_size_mailbox_name(&self) -> usize {
self.max_size_mailbox_name
}
pub fn max_size_attachments_per_email(&self) -> usize {
self.max_size_attachments_per_email
}
pub fn email_query_sort_options(&self) -> &[String] {
&self.email_query_sort_options
}
pub fn may_create_top_level_mailbox(&self) -> bool {
self.may_create_top_level_mailbox
}
}
impl SubmissionCapabilities {
pub fn max_delayed_send(&self) -> usize {
self.max_delayed_send
}
pub fn submission_extensions(&self) -> &[String] {
&self.submission_extensions
}
}