From 17cecbdb632b7ccab9d5e22fc8c60be9bc6cf230 Mon Sep 17 00:00:00 2001 From: Mauro D Date: Tue, 10 May 2022 15:22:13 +0000 Subject: [PATCH] Implementation of all JMAP Mail methods. --- src/blob/copy.rs | 47 ++- src/client.rs | 61 +++ src/core/changes.rs | 59 ++- src/core/copy.rs | 81 ++++ src/core/error.rs | 30 +- src/core/get.rs | 65 +++- src/core/query.rs | 182 ++++++++- src/core/query_changes.rs | 96 ++++- src/core/request.rs | 673 +++++++++++++++++++++++++++++++++- src/core/response.rs | 2 +- src/core/session.rs | 154 ++++++-- src/core/set.rs | 129 ++++++- src/email/import.rs | 124 +++++++ src/email/mod.rs | 131 ++++++- src/email/parse.rs | 126 +++++++ src/email/query.rs | 282 ++++++++++++++ src/email/search_snippet.rs | 0 src/email/set.rs | 104 ++++-- src/email_submission/mod.rs | 13 +- src/email_submission/query.rs | 108 ++++++ src/identity/mod.rs | 2 +- src/lib.rs | 3 +- src/mailbox/mod.rs | 17 +- src/mailbox/query.rs | 83 +++++ src/push_subscription/mod.rs | 2 +- src/thread/mod.rs | 8 + src/vacation_response/mod.rs | 18 + 27 files changed, 2501 insertions(+), 99 deletions(-) create mode 100644 src/client.rs create mode 100644 src/email/import.rs create mode 100644 src/email/parse.rs create mode 100644 src/email/query.rs create mode 100644 src/email/search_snippet.rs create mode 100644 src/email_submission/query.rs create mode 100644 src/mailbox/query.rs diff --git a/src/blob/copy.rs b/src/blob/copy.rs index ebb8ca6..e9dcfdf 100644 --- a/src/blob/copy.rs +++ b/src/blob/copy.rs @@ -15,7 +15,7 @@ pub struct CopyBlobRequest { } #[derive(Debug, Clone, Deserialize)] -pub struct CopyBlobResponse { +pub struct CopyBlobResponse { #[serde(rename = "fromAccountId")] from_account_id: String, #[serde(rename = "accountId")] @@ -23,5 +23,48 @@ pub struct CopyBlobResponse { #[serde(rename = "copied")] copied: Option>, #[serde(rename = "notCopied")] - not_copied: Option>>, + not_copied: Option>>, +} + +impl CopyBlobRequest { + pub fn new(from_account_id: String, account_id: String) -> Self { + CopyBlobRequest { + from_account_id, + account_id, + blob_ids: vec![], + } + } + + pub fn blob_id(&mut self, blob_id: String) -> &mut Self { + self.blob_ids.push(blob_id); + self + } +} + +impl CopyBlobResponse { + pub fn from_account_id(&self) -> &str { + &self.from_account_id + } + + pub fn account_id(&self) -> &str { + &self.account_id + } + + pub fn copied(&self) -> Option> { + self.copied.as_ref().map(|map| map.keys()) + } + + pub fn copied_details(&self, id: &str) -> Option<&str> { + self.copied + .as_ref() + .and_then(|map| map.get(id).map(|s| s.as_str())) + } + + pub fn not_copied(&self) -> Option> { + self.not_copied.as_ref().map(|map| map.keys()) + } + + pub fn not_copied_reason(&self, id: &str) -> Option<&SetError> { + self.not_copied.as_ref().and_then(|map| map.get(id)) + } } diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..0fc72c7 --- /dev/null +++ b/src/client.rs @@ -0,0 +1,61 @@ +use std::time::Duration; + +use crate::core::request::Request; + +const DEFAULT_TIMEOUT_MS: u64 = 10 * 1000; + +pub struct Client { + client: reqwest::ClientBuilder, + default_account_id: String, +} + +impl Client { + pub fn connect(url: &str) -> Self { + Client { + client: reqwest::Client::builder().timeout(Duration::from_millis(DEFAULT_TIMEOUT_MS)), + default_account_id: "co".to_string(), + } + } + + pub fn default_account_id(&self) -> &str { + &self.default_account_id + } + + pub fn request(&self) -> Request<'_> { + Request::new(self) + } +} + +#[cfg(test)] +mod tests { + + #[test] + fn test_serialize() { + /*let mut client = Client::connect("coco"); + + client.request().email_set().create( + "coco", + Email::new() + .from(["Pepe"]) + .subject("Hello world!") + .sent_at(342374), + );*/ + + /*let query: QueryRequest = + QueryRequest::new("coco".to_string()) + .filter(Filter::or([ + Filter::and([ + EmailFilter::in_mailbox("peperino"), + EmailFilter::in_mailbox_other_than(["coco", "miel"]), + EmailFilter::from("comoro"), + ]), + Filter::not([EmailFilter::after(428374234)]), + ])) + .sort([ + EmailComparator::has_keyword("cocomiel"), + EmailComparator::size(), + ]); + + println!("{}", serde_json::to_string_pretty(&query).unwrap());*/ + } +} diff --git a/src/core/changes.rs b/src/core/changes.rs index 6920990..97f2b87 100644 --- a/src/core/changes.rs +++ b/src/core/changes.rs @@ -11,7 +11,7 @@ pub struct ChangesRequest { } #[derive(Debug, Clone, Deserialize)] -pub struct ChangesResponse { +pub struct ChangesResponse { #[serde(rename = "accountId")] account_id: String, #[serde(rename = "oldState")] @@ -23,4 +23,61 @@ pub struct ChangesResponse { created: Vec, updated: Vec, destroyed: Vec, + + #[serde(flatten)] + arguments: A, +} + +impl ChangesRequest { + pub fn new(account_id: String, since_state: String) -> Self { + ChangesRequest { + account_id, + since_state, + max_changes: None, + } + } + + pub fn account_id(&mut self, account_id: impl Into) -> &mut Self { + self.account_id = account_id.into(); + self + } + + pub fn max_changes(&mut self, max_changes: usize) -> &mut Self { + self.max_changes = Some(max_changes); + self + } +} + +impl ChangesResponse { + pub fn account_id(&self) -> &str { + &self.account_id + } + + pub fn old_state(&self) -> &str { + &self.old_state + } + + pub fn new_state(&self) -> &str { + &self.new_state + } + + pub fn has_more_changes(&self) -> bool { + self.has_more_changes + } + + pub fn created(&self) -> &[String] { + &self.created + } + + pub fn updated(&self) -> &[String] { + &self.updated + } + + pub fn destroyed(&self) -> &[String] { + &self.destroyed + } + + pub fn arguments(&self) -> &A { + &self.arguments + } } diff --git a/src/core/copy.rs b/src/core/copy.rs index 3e61a87..94ce32d 100644 --- a/src/core/copy.rs +++ b/src/core/copy.rs @@ -8,16 +8,22 @@ use super::set::SetError; pub struct CopyRequest { #[serde(rename = "fromAccountId")] from_account_id: String, + #[serde(rename = "ifFromInState")] if_from_in_state: Option, + #[serde(rename = "accountId")] account_id: String, + #[serde(rename = "ifInState")] if_in_state: Option, + #[serde(rename = "create")] create: HashMap, + #[serde(rename = "onSuccessDestroyOriginal")] on_success_destroy_original: bool, + #[serde(rename = "destroyFromIfInState")] destroy_from_if_in_state: Option, } @@ -37,3 +43,78 @@ pub struct CopyResponse { #[serde(rename = "notCreated")] not_created: Option>>, } + +impl CopyRequest { + pub fn new(from_account_id: String, account_id: String) -> Self { + CopyRequest { + from_account_id, + if_from_in_state: None, + account_id, + if_in_state: None, + create: HashMap::new(), + on_success_destroy_original: false, + destroy_from_if_in_state: None, + } + } + + pub fn account_id(&mut self, account_id: impl Into) -> &mut Self { + self.account_id = account_id.into(); + self + } + + pub fn if_from_in_state(&mut self, if_from_in_state: impl Into) -> &mut Self { + self.if_from_in_state = Some(if_from_in_state.into()); + self + } + + pub fn if_in_state(&mut self, if_in_state: impl Into) -> &mut Self { + self.if_in_state = Some(if_in_state.into()); + self + } + + pub fn create(&mut self, id: impl Into, value: T) -> &mut Self { + self.create.insert(id.into(), value); + self + } + + pub fn on_success_destroy_original(&mut self, on_success_destroy_original: bool) -> &mut Self { + self.on_success_destroy_original = on_success_destroy_original; + self + } + + pub fn destroy_from_if_in_state( + &mut self, + destroy_from_if_in_state: impl Into, + ) -> &mut Self { + self.destroy_from_if_in_state = Some(destroy_from_if_in_state.into()); + self + } +} + +impl CopyResponse { + pub fn from_account_id(&self) -> &str { + &self.from_account_id + } + + pub fn account_id(&self) -> &str { + &self.account_id + } + + pub fn old_state(&self) -> Option<&str> { + self.old_state.as_deref() + } + + pub fn new_state(&self) -> &str { + &self.new_state + } + + pub fn created(&self, id: &str) -> Option<&T> { + self.created.as_ref().and_then(|created| created.get(id)) + } + + pub fn not_created(&self, id: &str) -> Option<&SetError> { + self.not_created + .as_ref() + .and_then(|not_created| not_created.get(id)) + } +} diff --git a/src/core/error.rs b/src/core/error.rs index 8a4a7e2..e933c16 100644 --- a/src/core/error.rs +++ b/src/core/error.rs @@ -1,4 +1,4 @@ -use serde::{Deserialize, Serialize}; +use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct ProblemDetails { @@ -71,3 +71,31 @@ pub enum MethodErrorType { #[serde(rename = "tooManyChanges")] TooManyChanges, } + +impl ProblemDetails { + pub fn error(&self) -> &ProblemType { + &self.p_type + } + + pub fn status(&self) -> Option { + self.status + } + + pub fn title(&self) -> Option<&str> { + self.title.as_deref() + } + + pub fn detail(&self) -> Option<&str> { + self.detail.as_deref() + } + + pub fn limit(&self) -> Option { + self.limit + } +} + +impl MethodError { + pub fn error(&self) -> &MethodErrorType { + &self.p_type + } +} diff --git a/src/core/get.rs b/src/core/get.rs index 2916888..f3e8e67 100644 --- a/src/core/get.rs +++ b/src/core/get.rs @@ -1,11 +1,14 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize)] -pub struct GetRequest { +pub struct GetRequest { #[serde(rename = "accountId")] account_id: String, ids: Option>, properties: Option>, + + #[serde(flatten)] + arguments: A, } #[derive(Debug, Clone, Deserialize)] @@ -17,3 +20,63 @@ pub struct GetResponse { #[serde(rename = "notFound")] not_found: Vec, } + +impl GetRequest { + pub fn new(account_id: String) -> Self { + GetRequest { + account_id, + ids: None, + properties: None, + arguments: A::default(), + } + } + + pub fn account_id(&mut self, account_id: impl Into) -> &mut Self { + self.account_id = account_id.into(); + self + } + + pub fn ids(&mut self, ids: U) -> &mut Self + where + U: IntoIterator, + V: Into, + { + self.ids = Some(ids.into_iter().map(|v| v.into()).collect()); + self + } + + pub fn properties(&mut self, properties: impl IntoIterator) -> &mut Self { + self.properties = Some(properties.into_iter().collect()); + self + } + + pub fn arguments(&mut self) -> &mut A { + &mut self.arguments + } +} + +impl GetResponse { + pub fn account_id(&self) -> &str { + &self.account_id + } + + pub fn state(&self) -> &str { + &self.state + } + + pub fn list(&self) -> &[T] { + &self.list + } + + pub fn not_found(&self) -> &[String] { + &self.not_found + } + + pub fn unwrap_list(&mut self) -> Vec { + std::mem::take(&mut self.list) + } + + pub fn unwrap_not_found(&mut self) -> Vec { + std::mem::take(&mut self.not_found) + } +} diff --git a/src/core/query.rs b/src/core/query.rs index 46003ee..a32f982 100644 --- a/src/core/query.rs +++ b/src/core/query.rs @@ -1,23 +1,33 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize)] -pub struct QueryRequest { +pub struct QueryRequest { #[serde(rename = "accountId")] account_id: String, + #[serde(rename = "filter")] - filter: Option>, + filter: Option>, + #[serde(rename = "sort")] - sort: Option>, + sort: Option>>, + #[serde(rename = "position")] position: i32, + #[serde(rename = "anchor")] anchor: Option, + #[serde(rename = "anchorOffset")] anchor_offset: i32, + #[serde(rename = "limit")] limit: Option, + #[serde(rename = "calculateTotal")] calculate_total: bool, + + #[serde(flatten)] + arguments: A, } #[derive(Debug, Clone, Serialize)] @@ -44,27 +54,189 @@ pub enum Operator { } #[derive(Debug, Clone, Serialize)] -pub struct Comparator { - property: T, +pub struct Comparator { #[serde(rename = "isAscending")] is_ascending: bool, + + #[serde(skip_serializing_if = "Option::is_none")] collation: Option, + + #[serde(flatten)] + arguments: A, } #[derive(Debug, Clone, Deserialize)] pub struct QueryResponse { #[serde(rename = "accountId")] account_id: String, + #[serde(rename = "queryState")] query_state: String, + #[serde(rename = "canCalculateChanges")] can_calculate_changes: bool, + #[serde(rename = "position")] position: i32, + #[serde(rename = "ids")] ids: Vec, + #[serde(rename = "total")] total: Option, + #[serde(rename = "limit")] limit: Option, } + +impl QueryRequest { + pub fn new(account_id: String) -> Self { + QueryRequest { + account_id, + filter: None, + sort: None, + position: 0, + anchor: None, + anchor_offset: 0, + limit: None, + calculate_total: false, + arguments: A::default(), + } + } + + pub fn account_id(&mut self, account_id: impl Into) -> &mut Self { + self.account_id = account_id.into(); + self + } + + pub fn filter(&mut self, filter: impl Into>) -> &mut Self { + self.filter = Some(filter.into()); + self + } + + pub fn sort(&mut self, sort: impl IntoIterator>) -> &mut Self { + self.sort = Some(sort.into_iter().collect()); + self + } + + pub fn position(&mut self, position: i32) -> &mut Self { + self.position = position; + self + } + + pub fn anchor(&mut self, anchor: impl Into) -> &mut Self { + self.anchor = Some(anchor.into()); + self + } + + pub fn anchor_offset(&mut self, anchor_offset: i32) -> &mut Self { + self.anchor_offset = anchor_offset; + self + } + + pub fn limit(&mut self, limit: usize) -> &mut Self { + self.limit = Some(limit); + self + } + + pub fn arguments(&mut self) -> &mut A { + &mut self.arguments + } +} + +impl QueryResponse { + pub fn account_id(&self) -> &str { + &self.account_id + } + + pub fn ids(&self) -> &[String] { + &self.ids + } + + pub fn total(&self) -> Option { + self.total + } + + pub fn limit(&self) -> Option { + self.limit + } + + pub fn position(&self) -> i32 { + self.position + } + + pub fn query_state(&self) -> &str { + &self.query_state + } + + pub fn can_calculate_changes(&self) -> bool { + self.can_calculate_changes + } +} + +impl Comparator { + pub fn new(arguments: A) -> Self { + Comparator { + is_ascending: true, + collation: None, + arguments, + } + } + + pub fn is_ascending(mut self, is_ascending: bool) -> Self { + self.is_ascending = is_ascending; + self + } + + pub fn collation(mut self, collation: String) -> Self { + self.collation = Some(collation); + self + } +} + +impl From> for Filter { + fn from(filter: FilterOperator) -> Self { + Filter::FilterOperator(filter) + } +} + +impl From for Filter { + fn from(filter: T) -> Self { + Filter::FilterCondition(filter) + } +} + +impl Filter { + pub fn and(conditions: U) -> Self + where + U: IntoIterator, + V: Into>, + { + Filter::FilterOperator(FilterOperator { + operator: Operator::And, + conditions: conditions.into_iter().map(|t| t.into()).collect(), + }) + } + + pub fn or(conditions: U) -> Self + where + U: IntoIterator, + V: Into>, + { + Filter::FilterOperator(FilterOperator { + operator: Operator::Or, + conditions: conditions.into_iter().map(|t| t.into()).collect(), + }) + } + + pub fn not(conditions: U) -> Self + where + U: IntoIterator, + V: Into>, + { + Filter::FilterOperator(FilterOperator { + operator: Operator::Not, + conditions: conditions.into_iter().map(|t| t.into()).collect(), + }) + } +} diff --git a/src/core/query_changes.rs b/src/core/query_changes.rs index 9ed5dd3..51a334c 100644 --- a/src/core/query_changes.rs +++ b/src/core/query_changes.rs @@ -1,15 +1,15 @@ use serde::{Deserialize, Serialize}; -use super::query::Filter; +use super::query::{Comparator, Filter}; #[derive(Debug, Clone, Serialize)] -pub struct QueryChangesRequest { +pub struct QueryChangesRequest { #[serde(rename = "accountId")] account_id: String, #[serde(rename = "filter")] - filter: Option>, + filter: Option>, #[serde(rename = "sort")] - sort: Option>, + sort: Option>>, #[serde(rename = "sinceQueryState")] since_query_state: String, #[serde(rename = "maxChanges")] @@ -18,6 +18,9 @@ pub struct QueryChangesRequest { up_to_id: Option, #[serde(rename = "calculateTotal")] calculate_total: bool, + + #[serde(flatten)] + arguments: A, } #[derive(Debug, Clone, Deserialize)] @@ -41,3 +44,88 @@ pub struct AddedItem { id: String, index: usize, } + +impl QueryChangesRequest { + pub fn new(account_id: String, since_query_state: String) -> Self { + QueryChangesRequest { + account_id, + filter: None, + sort: None, + since_query_state, + max_changes: None, + up_to_id: None, + calculate_total: false, + arguments: A::default(), + } + } + + pub fn account_id(&mut self, account_id: impl Into) -> &mut Self { + self.account_id = account_id.into(); + self + } + + pub fn filter(&mut self, filter: impl Into>) -> &mut Self { + self.filter = Some(filter.into()); + self + } + + pub fn sort(&mut self, sort: impl IntoIterator>) -> &mut Self { + self.sort = Some(sort.into_iter().collect()); + self + } + + pub fn max_changes(&mut self, max_changes: usize) -> &mut Self { + self.max_changes = Some(max_changes); + self + } + + pub fn up_to_id(&mut self, up_to_id: impl Into) -> &mut Self { + self.up_to_id = Some(up_to_id.into()); + self + } + + pub fn calculate_total(&mut self, calculate_total: bool) -> &mut Self { + self.calculate_total = calculate_total; + self + } + + pub fn arguments(&mut self) -> &mut A { + &mut self.arguments + } +} + +impl QueryChangesResponse { + pub fn account_id(&self) -> &str { + &self.account_id + } + + pub fn old_query_state(&self) -> &str { + &self.old_query_state + } + + pub fn new_query_state(&self) -> &str { + &self.new_query_state + } + + pub fn total(&self) -> Option { + self.total + } + + pub fn removed(&self) -> &[String] { + &self.removed + } + + pub fn added(&self) -> &[AddedItem] { + &self.added + } +} + +impl AddedItem { + pub fn id(&self) -> &str { + &self.id + } + + pub fn index(&self) -> usize { + self.index + } +} diff --git a/src/core/request.rs b/src/core/request.rs index e087d75..3154e28 100644 --- a/src/core/request.rs +++ b/src/core/request.rs @@ -2,10 +2,29 @@ use std::collections::HashMap; use serde::Serialize; -use crate::{Method, URI}; +use crate::{ + blob::copy::CopyBlobRequest, + client::Client, + email::{self, import::EmailImportRequest, parse::EmailParseRequest, Email}, + email_submission::{self, EmailSubmission}, + identity::{self, Identity}, + mailbox::{self, Mailbox}, + push_subscription::{self, PushSubscription}, + thread, + vacation_response::{self, VacationResponse}, + Method, Set, URI, +}; + +use super::{ + changes::ChangesRequest, copy::CopyRequest, get::GetRequest, query::QueryRequest, + query_changes::QueryChangesRequest, set::SetRequest, +}; + +#[derive(Serialize)] +pub struct Request<'x> { + #[serde(skip)] + client: &'x Client, -#[derive(Debug, Clone, Serialize)] -pub struct Request { using: Vec, #[serde(rename = "methodCalls")] method_calls: Vec<(Method, Arguments, String)>, @@ -23,4 +42,650 @@ pub struct ResultReference { } #[derive(Debug, Clone, Serialize)] -pub struct Arguments {} +#[serde(untagged)] +pub enum Arguments { + Changes(ChangesRequest), + PushGet(GetRequest), + PushSet(SetRequest, ()>), + BlobCopy(CopyBlobRequest), + MailboxGet(GetRequest), + MailboxQuery( + QueryRequest, + ), + MailboxQueryChanges( + QueryChangesRequest< + mailbox::query::Filter, + mailbox::query::Comparator, + mailbox::QueryArguments, + >, + ), + MailboxSet(SetRequest, mailbox::SetArguments>), + ThreadGet(GetRequest), + EmailGet(GetRequest), + EmailQuery(QueryRequest), + EmailQueryChanges( + QueryChangesRequest, + ), + EmailSet(SetRequest, ()>), + EmailCopy(CopyRequest>), + EmailImport(EmailImportRequest), + EmailParse(EmailParseRequest), + IdentityGet(GetRequest), + IdentitySet(SetRequest, ()>), + EmailSubmissionGet(GetRequest), + EmailSubmissionQuery( + QueryRequest, + ), + EmailSubmissionQueryChanges( + QueryChangesRequest< + email_submission::query::Filter, + email_submission::query::Comparator, + (), + >, + ), + EmailSubmissionSet(SetRequest, email_submission::SetArguments>), + VacationResponseGet(GetRequest), + VacationResponseSet(SetRequest, ()>), +} + +impl Arguments { + pub fn changes(account_id: String, since_state: String) -> Self { + Arguments::Changes(ChangesRequest::new(account_id, since_state)) + } + + pub fn push_get(account_id: String) -> Self { + Arguments::PushGet(GetRequest::new(account_id)) + } + + pub fn push_set(account_id: String) -> Self { + Arguments::PushSet(SetRequest::new(account_id)) + } + + pub fn blob_copy(from_account_id: String, account_id: String) -> Self { + Arguments::BlobCopy(CopyBlobRequest::new(from_account_id, account_id)) + } + + pub fn mailbox_get(account_id: String) -> Self { + Arguments::MailboxGet(GetRequest::new(account_id)) + } + + pub fn mailbox_query(account_id: String) -> Self { + Arguments::MailboxQuery(QueryRequest::new(account_id)) + } + + pub fn mailbox_query_changes(account_id: String, since_query_state: String) -> Self { + Arguments::MailboxQueryChanges(QueryChangesRequest::new(account_id, since_query_state)) + } + + pub fn mailbox_set(account_id: String) -> Self { + Arguments::MailboxSet(SetRequest::new(account_id)) + } + + pub fn thread_get(account_id: String) -> Self { + Arguments::ThreadGet(GetRequest::new(account_id)) + } + + pub fn email_get(account_id: String) -> Self { + Arguments::EmailGet(GetRequest::new(account_id)) + } + + pub fn email_query(account_id: String) -> Self { + Arguments::EmailQuery(QueryRequest::new(account_id)) + } + + pub fn email_query_changes(account_id: String, since_query_state: String) -> Self { + Arguments::EmailQueryChanges(QueryChangesRequest::new(account_id, since_query_state)) + } + + pub fn email_set(account_id: String) -> Self { + Arguments::EmailSet(SetRequest::new(account_id)) + } + + pub fn email_copy(from_account_id: String, account_id: String) -> Self { + Arguments::EmailCopy(CopyRequest::new(from_account_id, account_id)) + } + + pub fn email_import(account_id: String) -> Self { + Arguments::EmailImport(EmailImportRequest::new(account_id)) + } + + pub fn email_parse(account_id: String) -> Self { + Arguments::EmailParse(EmailParseRequest::new(account_id)) + } + + pub fn identity_get(account_id: String) -> Self { + Arguments::IdentityGet(GetRequest::new(account_id)) + } + + pub fn identity_set(account_id: String) -> Self { + Arguments::IdentitySet(SetRequest::new(account_id)) + } + + pub fn email_submission_get(account_id: String) -> Self { + Arguments::EmailSubmissionGet(GetRequest::new(account_id)) + } + + pub fn email_submission_query(account_id: String) -> Self { + Arguments::EmailSubmissionQuery(QueryRequest::new(account_id)) + } + + pub fn email_submission_query_changes(account_id: String, since_query_state: String) -> Self { + Arguments::EmailSubmissionQueryChanges(QueryChangesRequest::new( + account_id, + since_query_state, + )) + } + + pub fn email_submission_set(account_id: String) -> Self { + Arguments::EmailSubmissionSet(SetRequest::new(account_id)) + } + + pub fn vacation_response_get(account_id: String) -> Self { + Arguments::VacationResponseGet(GetRequest::new(account_id)) + } + + pub fn vacation_response_set(account_id: String) -> Self { + Arguments::VacationResponseSet(SetRequest::new(account_id)) + } + + pub fn changes_mut(&mut self) -> &mut ChangesRequest { + match self { + Arguments::Changes(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn push_get_mut(&mut self) -> &mut GetRequest { + match self { + Arguments::PushGet(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn push_set_mut(&mut self) -> &mut SetRequest, ()> { + match self { + Arguments::PushSet(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn blob_copy_mut(&mut self) -> &mut CopyBlobRequest { + match self { + Arguments::BlobCopy(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn mailbox_get_mut(&mut self) -> &mut GetRequest { + match self { + Arguments::MailboxGet(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn mailbox_query_mut( + &mut self, + ) -> &mut QueryRequest< + mailbox::query::Filter, + mailbox::query::Comparator, + mailbox::QueryArguments, + > { + match self { + Arguments::MailboxQuery(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn mailbox_query_changes_mut( + &mut self, + ) -> &mut QueryChangesRequest< + mailbox::query::Filter, + mailbox::query::Comparator, + mailbox::QueryArguments, + > { + match self { + Arguments::MailboxQueryChanges(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn mailbox_set_mut(&mut self) -> &mut SetRequest, mailbox::SetArguments> { + match self { + Arguments::MailboxSet(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn thread_get_mut(&mut self) -> &mut GetRequest { + match self { + Arguments::ThreadGet(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn email_get_mut(&mut self) -> &mut GetRequest { + match self { + Arguments::EmailGet(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn email_query_mut( + &mut self, + ) -> &mut QueryRequest + { + match self { + Arguments::EmailQuery(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn email_query_changes_mut( + &mut self, + ) -> &mut QueryChangesRequest< + email::query::Filter, + email::query::Comparator, + email::QueryArguments, + > { + match self { + Arguments::EmailQueryChanges(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn email_set_mut(&mut self) -> &mut SetRequest, ()> { + match self { + Arguments::EmailSet(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn email_copy_mut(&mut self) -> &mut CopyRequest> { + match self { + Arguments::EmailCopy(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn email_import_mut(&mut self) -> &mut EmailImportRequest { + match self { + Arguments::EmailImport(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn email_parse_mut(&mut self) -> &mut EmailParseRequest { + match self { + Arguments::EmailParse(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn identity_get_mut(&mut self) -> &mut GetRequest { + match self { + Arguments::IdentityGet(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn identity_set_mut(&mut self) -> &mut SetRequest, ()> { + match self { + Arguments::IdentitySet(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn email_submission_get_mut(&mut self) -> &mut GetRequest { + match self { + Arguments::EmailSubmissionGet(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn email_submission_query_mut( + &mut self, + ) -> &mut QueryRequest + { + match self { + Arguments::EmailSubmissionQuery(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn email_submission_query_changes_mut( + &mut self, + ) -> &mut QueryChangesRequest< + email_submission::query::Filter, + email_submission::query::Comparator, + (), + > { + match self { + Arguments::EmailSubmissionQueryChanges(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn email_submission_set_mut( + &mut self, + ) -> &mut SetRequest, email_submission::SetArguments> { + match self { + Arguments::EmailSubmissionSet(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn vacation_response_get_mut( + &mut self, + ) -> &mut GetRequest { + match self { + Arguments::VacationResponseGet(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn vacation_response_set_mut(&mut self) -> &mut SetRequest, ()> { + match self { + Arguments::VacationResponseSet(ref mut r) => r, + _ => unreachable!(), + } + } +} + +impl<'x> Request<'x> { + pub fn new(client: &'x Client) -> Self { + Request { + using: vec![URI::Core, URI::Mail], + method_calls: vec![], + created_ids: None, + client, + } + } + + fn add_method_call(&mut self, method: Method, arguments: Arguments) -> &mut Arguments { + let call_id = format!("s{}", self.method_calls.len()); + self.method_calls.push((method, arguments, call_id)); + &mut self.method_calls.last_mut().unwrap().1 + } + + pub fn get_push(&mut self) -> &mut GetRequest { + self.add_method_call( + Method::GetPushSubscription, + Arguments::push_get(self.client.default_account_id().to_string()), + ) + .push_get_mut() + } + + pub fn set_push(&mut self) -> &mut SetRequest, ()> { + self.add_method_call( + Method::SetPushSubscription, + Arguments::push_set(self.client.default_account_id().to_string()), + ) + .push_set_mut() + } + + pub fn copy_blob( + &mut self, + from_account_id: impl Into, + account_id: impl Into, + ) -> &mut CopyBlobRequest { + self.add_method_call( + Method::CopyBlob, + Arguments::blob_copy(from_account_id.into(), account_id.into()), + ) + .blob_copy_mut() + } + + pub fn get_mailbox(&mut self) -> &mut GetRequest { + self.add_method_call( + Method::GetMailbox, + Arguments::mailbox_get(self.client.default_account_id().to_string()), + ) + .mailbox_get_mut() + } + + pub fn changes_mailbox(&mut self, since_state: impl Into) -> &mut ChangesRequest { + self.add_method_call( + Method::ChangesMailbox, + Arguments::changes( + self.client.default_account_id().to_string(), + since_state.into(), + ), + ) + .changes_mut() + } + + pub fn query_mailbox( + &mut self, + ) -> &mut QueryRequest< + mailbox::query::Filter, + mailbox::query::Comparator, + mailbox::QueryArguments, + > { + self.add_method_call( + Method::QueryMailbox, + Arguments::mailbox_query(self.client.default_account_id().to_string()), + ) + .mailbox_query_mut() + } + + pub fn query_mailbox_changes( + &mut self, + since_query_state: impl Into, + ) -> &mut QueryChangesRequest< + mailbox::query::Filter, + mailbox::query::Comparator, + mailbox::QueryArguments, + > { + self.add_method_call( + Method::QueryChangesMailbox, + Arguments::mailbox_query_changes( + self.client.default_account_id().to_string(), + since_query_state.into(), + ), + ) + .mailbox_query_changes_mut() + } + + pub fn set_mailbox(&mut self) -> &mut SetRequest, mailbox::SetArguments> { + self.add_method_call( + Method::SetMailbox, + Arguments::mailbox_set(self.client.default_account_id().to_string()), + ) + .mailbox_set_mut() + } + + pub fn get_thread(&mut self) -> &mut GetRequest { + self.add_method_call( + Method::GetThread, + Arguments::thread_get(self.client.default_account_id().to_string()), + ) + .thread_get_mut() + } + + pub fn changes_thread(&mut self, since_state: impl Into) -> &mut ChangesRequest { + self.add_method_call( + Method::ChangesThread, + Arguments::changes( + self.client.default_account_id().to_string(), + since_state.into(), + ), + ) + .changes_mut() + } + pub fn get_email(&mut self) -> &mut GetRequest { + self.add_method_call( + Method::GetEmail, + Arguments::email_get(self.client.default_account_id().to_string()), + ) + .email_get_mut() + } + + pub fn changes_email(&mut self, since_state: impl Into) -> &mut ChangesRequest { + self.add_method_call( + Method::ChangesEmail, + Arguments::changes( + self.client.default_account_id().to_string(), + since_state.into(), + ), + ) + .changes_mut() + } + + pub fn query_email( + &mut self, + ) -> &mut QueryRequest + { + self.add_method_call( + Method::QueryEmail, + Arguments::email_query(self.client.default_account_id().to_string()), + ) + .email_query_mut() + } + + pub fn query_email_changes( + &mut self, + since_query_state: impl Into, + ) -> &mut QueryChangesRequest< + email::query::Filter, + email::query::Comparator, + email::QueryArguments, + > { + self.add_method_call( + Method::QueryChangesEmail, + Arguments::email_query_changes( + self.client.default_account_id().to_string(), + since_query_state.into(), + ), + ) + .email_query_changes_mut() + } + + pub fn set_email(&mut self) -> &mut SetRequest, ()> { + self.add_method_call( + Method::SetEmail, + Arguments::email_set(self.client.default_account_id().to_string()), + ) + .email_set_mut() + } + + pub fn copy_email( + &mut self, + from_account_id: impl Into, + account_id: impl Into, + ) -> &mut CopyRequest> { + self.add_method_call( + Method::CopyEmail, + Arguments::email_copy(from_account_id.into(), account_id.into()), + ) + .email_copy_mut() + } + + pub fn import_email(&mut self) -> &mut EmailImportRequest { + self.add_method_call( + Method::ImportEmail, + Arguments::email_import(self.client.default_account_id().to_string()), + ) + .email_import_mut() + } + + pub fn parse_email(&mut self) -> &mut EmailParseRequest { + self.add_method_call( + Method::ParseEmail, + Arguments::email_parse(self.client.default_account_id().to_string()), + ) + .email_parse_mut() + } + + pub fn get_identity(&mut self) -> &mut GetRequest { + self.add_method_call( + Method::GetIdentity, + Arguments::identity_get(self.client.default_account_id().to_string()), + ) + .identity_get_mut() + } + + pub fn set_identity(&mut self) -> &mut SetRequest, ()> { + self.add_method_call( + Method::SetIdentity, + Arguments::identity_set(self.client.default_account_id().to_string()), + ) + .identity_set_mut() + } + + pub fn get_email_submission(&mut self) -> &mut GetRequest { + self.add_method_call( + Method::GetEmailSubmission, + Arguments::email_submission_get(self.client.default_account_id().to_string()), + ) + .email_submission_get_mut() + } + + pub fn changes_email_submission( + &mut self, + since_state: impl Into, + ) -> &mut ChangesRequest { + self.add_method_call( + Method::ChangesEmailSubmission, + Arguments::changes( + self.client.default_account_id().to_string(), + since_state.into(), + ), + ) + .changes_mut() + } + + pub fn query_email_submission( + &mut self, + ) -> &mut QueryRequest + { + self.add_method_call( + Method::QueryEmailSubmission, + Arguments::email_submission_query(self.client.default_account_id().to_string()), + ) + .email_submission_query_mut() + } + + pub fn query_email_submission_changes( + &mut self, + since_query_state: impl Into, + ) -> &mut QueryChangesRequest< + email_submission::query::Filter, + email_submission::query::Comparator, + (), + > { + self.add_method_call( + Method::QueryChangesEmailSubmission, + Arguments::email_submission_query_changes( + self.client.default_account_id().to_string(), + since_query_state.into(), + ), + ) + .email_submission_query_changes_mut() + } + + pub fn set_email_submission( + &mut self, + ) -> &mut SetRequest, email_submission::SetArguments> { + self.add_method_call( + Method::SetEmailSubmission, + Arguments::email_submission_set(self.client.default_account_id().to_string()), + ) + .email_submission_set_mut() + } + + pub fn get_vacation_response(&mut self) -> &mut GetRequest { + self.add_method_call( + Method::GetVacationResponse, + Arguments::vacation_response_get(self.client.default_account_id().to_string()), + ) + .vacation_response_get_mut() + } + + pub fn set_vacation_response(&mut self) -> &mut SetRequest, ()> { + self.add_method_call( + Method::SetVacationResponse, + Arguments::vacation_response_set(self.client.default_account_id().to_string()), + ) + .vacation_response_set_mut() + } +} diff --git a/src/core/response.rs b/src/core/response.rs index 6c06f02..481adf1 100644 --- a/src/core/response.rs +++ b/src/core/response.rs @@ -5,7 +5,7 @@ use serde::Deserialize; use crate::Method; #[derive(Debug, Clone, Deserialize)] -pub struct Request { +pub struct Response { #[serde(rename = "methodResponses")] method_calls: Vec<(Method, Result, String)>, #[serde(rename = "createdIds")] diff --git a/src/core/session.rs b/src/core/session.rs index 8c4e880..e241aa1 100644 --- a/src/core/session.rs +++ b/src/core/session.rs @@ -2,24 +2,34 @@ use std::collections::HashMap; use serde::Deserialize; +use crate::email::{MailCapabilities, SubmissionCapabilities}; + #[derive(Debug, Clone, Deserialize)] pub struct Session { #[serde(rename = "capabilities")] capabilities: HashMap, + #[serde(rename = "accounts")] accounts: HashMap, + #[serde(rename = "primaryAccounts")] primary_accounts: HashMap, + #[serde(rename = "username")] username: String, + #[serde(rename = "apiUrl")] api_url: String, + #[serde(rename = "downloadUrl")] download_url: String, + #[serde(rename = "uploadUrl")] upload_url: String, + #[serde(rename = "eventSourceUrl")] event_source_url: String, + #[serde(rename = "state")] state: String, } @@ -28,21 +38,24 @@ pub struct Session { pub struct Account { #[serde(rename = "name")] name: String, + #[serde(rename = "isPersonal")] is_personal: bool, + #[serde(rename = "isReadOnly")] is_read_only: bool, + #[serde(rename = "accountCapabilities")] account_capabilities: HashMap, } #[derive(Debug, Clone, Deserialize)] #[serde(untagged)] -enum Capabilities { +pub enum Capabilities { Core(CoreCapabilities), Mail(MailCapabilities), Submission(SubmissionCapabilities), - EmptyCapabilities(EmptyCapabilities), + Empty(EmptyCapabilities), Other(serde_json::Value), } @@ -50,45 +63,134 @@ enum Capabilities { pub struct CoreCapabilities { #[serde(rename = "maxSizeUpload")] max_size_upload: usize, + #[serde(rename = "maxConcurrentUpload")] max_concurrent_upload: usize, + #[serde(rename = "maxSizeRequest")] max_size_request: usize, + #[serde(rename = "maxConcurrentRequests")] max_concurrent_requests: usize, + #[serde(rename = "maxCallsInRequest")] max_calls_in_request: usize, + #[serde(rename = "maxObjectsInGet")] max_objects_in_get: usize, + #[serde(rename = "maxObjectsInSet")] max_objects_in_set: usize, + #[serde(rename = "collationAlgorithms")] collation_algorithms: Vec, } -#[derive(Debug, Clone, Deserialize)] -pub struct MailCapabilities { - #[serde(rename = "maxMailboxesPerEmail")] - max_mailboxes_per_email: Option, - #[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, - #[serde(rename = "mayCreateTopLevelMailbox")] - may_create_top_level_mailbox: bool, -} - -#[derive(Debug, Clone, Deserialize)] -pub struct SubmissionCapabilities { - #[serde(rename = "maxDelayedSend")] - max_delayed_send: usize, - #[serde(rename = "submissionExtensions")] - submission_extensions: Vec, -} - #[derive(Debug, Clone, Deserialize)] pub struct EmptyCapabilities {} + +impl Session { + pub fn capabilities(&self) -> impl Iterator { + self.capabilities.keys() + } + + pub fn capability(&self, capability: &str) -> Option<&Capabilities> { + self.capabilities.get(capability) + } + + pub fn accounts(&self) -> impl Iterator { + self.accounts.keys() + } + + pub fn account(&self, account: &str) -> Option<&Account> { + self.accounts.get(account) + } + + pub fn primary_accounts(&self) -> impl Iterator { + self.primary_accounts.keys() + } + + pub fn primary_account(&self, account: &str) -> Option<&String> { + self.primary_accounts.get(account) + } + + pub fn username(&self) -> &str { + &self.username + } + + pub fn api_url(&self) -> &str { + &self.api_url + } + + pub fn download_url(&self) -> &str { + &self.download_url + } + + pub fn upload_url(&self) -> &str { + &self.upload_url + } + + pub fn event_source_url(&self) -> &str { + &self.event_source_url + } + + pub fn state(&self) -> &str { + &self.state + } +} + +impl Account { + pub fn name(&self) -> &str { + &self.name + } + + pub fn is_personal(&self) -> bool { + self.is_personal + } + + pub fn is_read_only(&self) -> bool { + self.is_read_only + } + + pub fn capabilities(&self) -> impl Iterator { + self.account_capabilities.keys() + } + + pub fn capability(&self, capability: &str) -> Option<&Capabilities> { + self.account_capabilities.get(capability) + } +} + +impl CoreCapabilities { + pub fn max_size_upload(&self) -> usize { + self.max_size_upload + } + + pub fn max_concurrent_upload(&self) -> usize { + self.max_concurrent_upload + } + + pub fn max_size_request(&self) -> usize { + self.max_size_request + } + + pub fn max_concurrent_requests(&self) -> usize { + self.max_concurrent_requests + } + + pub fn max_calls_in_request(&self) -> usize { + self.max_calls_in_request + } + + pub fn max_objects_in_get(&self) -> usize { + self.max_objects_in_get + } + + pub fn max_objects_in_set(&self) -> usize { + self.max_objects_in_set + } + + pub fn collation_algorithms(&self) -> &[String] { + &self.collation_algorithms + } +} diff --git a/src/core/set.rs b/src/core/set.rs index 5d13b3c..7acd2d2 100644 --- a/src/core/set.rs +++ b/src/core/set.rs @@ -1,20 +1,19 @@ use chrono::{DateTime, NaiveDateTime, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use std::hash::Hash; #[derive(Debug, Clone, Serialize)] -pub struct SetRequest -where - U: Eq + Hash, -{ +pub struct SetRequest { #[serde(rename = "accountId")] account_id: String, #[serde(rename = "ifInState")] if_in_state: Option, create: Option>, - update: Option>>, + update: Option>, destroy: Option>, + + #[serde(flatten)] + arguments: A, } #[derive(Debug, Clone, Deserialize)] @@ -110,3 +109,121 @@ pub fn date_not_set(date: &Option>) -> bool { pub fn list_not_set(list: &Option>) -> bool { matches!(list, Some(list) if list.is_empty() ) } + +impl SetRequest { + pub fn new(account_id: String) -> Self { + Self { + account_id, + if_in_state: None, + create: None, + update: None, + destroy: None, + arguments: Default::default(), + } + } + + pub fn account_id(&mut self, account_id: impl Into) -> &mut Self { + self.account_id = account_id.into(); + self + } + + pub fn if_in_state(&mut self, if_in_state: impl Into) -> &mut Self { + self.if_in_state = Some(if_in_state.into()); + self + } + + pub fn create(&mut self, id: impl Into, value: T) -> &mut Self { + self.create + .get_or_insert_with(HashMap::new) + .insert(id.into(), value); + self + } + + pub fn update(&mut self, id: impl Into, value: T) -> &mut Self { + self.update + .get_or_insert_with(HashMap::new) + .insert(id.into(), value); + self + } + + pub fn destroy(&mut self, id: impl Into) -> &mut Self { + self.destroy.get_or_insert_with(Vec::new).push(id.into()); + self + } + + pub fn arguments(&mut self) -> &mut A { + &mut self.arguments + } +} + +impl SetResponse { + pub fn account_id(&self) -> &str { + &self.account_id + } + + pub fn old_state(&self) -> Option<&str> { + self.old_state.as_deref() + } + + pub fn new_state(&self) -> &str { + &self.new_state + } + + pub fn created(&self) -> Option> { + self.created.as_ref().map(|map| map.keys()) + } + + pub fn updated(&self) -> Option> { + self.updated.as_ref().map(|map| map.keys()) + } + + pub fn destroyed(&self) -> Option<&[String]> { + self.destroyed.as_deref() + } + + pub fn not_created(&self) -> Option> { + self.not_created.as_ref().map(|map| map.keys()) + } + + pub fn not_updated(&self) -> Option> { + self.not_updated.as_ref().map(|map| map.keys()) + } + + pub fn not_destroyed(&self) -> Option> { + self.not_destroyed.as_ref().map(|map| map.keys()) + } + + pub fn not_created_reason(&self, id: &str) -> Option<&SetError> { + self.not_created.as_ref().and_then(|map| map.get(id)) + } + + pub fn not_updated_reason(&self, id: &str) -> Option<&SetError> { + self.not_updated.as_ref().and_then(|map| map.get(id)) + } + + pub fn not_destroyed_reason(&self, id: &str) -> Option<&SetError> { + self.not_destroyed.as_ref().and_then(|map| map.get(id)) + } + + pub fn created_details(&self, id: &str) -> Option<&T> { + self.created.as_ref().and_then(|map| map.get(id)) + } + + pub fn updated_details(&self, id: &str) -> Option<&T> { + self.updated.as_ref().and_then(|map| map.get(id))?.as_ref() + } +} + +impl SetError { + pub fn error(&self) -> &SetErrorType { + &self.type_ + } + + pub fn description(&self) -> Option<&str> { + self.description.as_deref() + } + + pub fn properties(&self) -> Option<&[U]> { + self.properties.as_deref() + } +} diff --git a/src/email/import.rs b/src/email/import.rs new file mode 100644 index 0000000..a8016ab --- /dev/null +++ b/src/email/import.rs @@ -0,0 +1,124 @@ +use std::collections::HashMap; + +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; + +use crate::core::set::{from_timestamp, SetError}; + +use super::{Email, Property}; + +#[derive(Debug, Clone, Serialize)] +pub struct EmailImportRequest { + #[serde(rename = "accountId")] + account_id: String, + #[serde(rename = "ifInState")] + if_in_state: Option, + + emails: HashMap, +} + +#[derive(Debug, Clone, Serialize)] +pub struct EmailImport { + #[serde(rename = "blobId")] + blob_id: String, + + #[serde(rename = "mailboxIds")] + mailbox_ids: HashMap, + + #[serde(rename = "keywords")] + keywords: HashMap, + + #[serde(rename = "receivedAt")] + #[serde(skip_serializing_if = "Option::is_none")] + received_at: Option>, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct EmailImportResponse { + #[serde(rename = "accountId")] + account_id: String, + #[serde(rename = "oldState")] + old_state: Option, + #[serde(rename = "newState")] + new_state: String, + #[serde(rename = "created")] + created: Option>, + #[serde(rename = "notCreated")] + not_created: Option>>, +} + +impl EmailImportRequest { + pub fn new(account_id: String) -> Self { + EmailImportRequest { + account_id, + if_in_state: None, + emails: HashMap::new(), + } + } + + pub fn if_in_state(&mut self, if_in_state: String) -> &mut Self { + self.if_in_state = Some(if_in_state); + self + } + + pub fn add_email(&mut self, id: String, email_import: EmailImport) -> &mut Self { + self.emails.insert(id, email_import); + self + } +} + +impl EmailImport { + pub fn new(blob_id: String) -> Self { + EmailImport { + blob_id, + mailbox_ids: HashMap::new(), + keywords: HashMap::new(), + received_at: None, + } + } + + pub fn mailbox_id(mut self, mailbox_id: String) -> Self { + self.mailbox_ids.insert(mailbox_id, true); + self + } + + pub fn keyword(mut self, keyword: String) -> Self { + self.keywords.insert(keyword, true); + self + } + + pub fn received_at(mut self, received_at: i64) -> Self { + self.received_at = Some(from_timestamp(received_at)); + self + } +} + +impl EmailImportResponse { + pub fn account_id(&self) -> &str { + &self.account_id + } + + pub fn old_state(&self) -> Option<&str> { + self.old_state.as_deref() + } + + pub fn new_state(&self) -> &str { + &self.new_state + } + + pub fn created(&self) -> Option> { + self.created.as_ref().map(|map| map.keys()) + } + + pub fn not_created(&self) -> Option> { + self.not_created.as_ref().map(|map| map.keys()) + } + + pub fn created_details(&self, id: &str) -> Option<&Email> { + self.created.as_ref().and_then(|map| map.get(id)) + } + + pub fn not_created_reason(&self, id: &str) -> Option<&SetError> { + self.not_created.as_ref().and_then(|map| map.get(id)) + } +} diff --git a/src/email/mod.rs b/src/email/mod.rs index 8e2545d..fe77eeb 100644 --- a/src/email/mod.rs +++ b/src/email/mod.rs @@ -1,4 +1,8 @@ pub mod get; +pub mod import; +pub mod parse; +pub mod query; +pub mod search_snippet; pub mod set; use chrono::{DateTime, Utc}; @@ -225,7 +229,7 @@ pub struct EmailHeader { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum EmailProperty { +pub enum Property { #[serde(rename = "id")] Id, #[serde(rename = "blobId")] @@ -279,7 +283,7 @@ pub enum EmailProperty { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum EmailBodyProperty { +pub enum BodyProperty { #[serde(rename = "partId")] PartId, #[serde(rename = "blobId")] @@ -305,3 +309,126 @@ pub enum EmailBodyProperty { #[serde(rename = "subParts")] SubParts, } + +#[derive(Debug, Clone, Deserialize)] +pub struct MailCapabilities { + #[serde(rename = "maxMailboxesPerEmail")] + max_mailboxes_per_email: Option, + + #[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, + + #[serde(rename = "mayCreateTopLevelMailbox")] + may_create_top_level_mailbox: bool, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct SubmissionCapabilities { + #[serde(rename = "maxDelayedSend")] + max_delayed_send: usize, + + #[serde(rename = "submissionExtensions")] + submission_extensions: Vec, +} + +#[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>, + #[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, + ) -> &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 { + 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 + } +} diff --git a/src/email/parse.rs b/src/email/parse.rs new file mode 100644 index 0000000..b9ec5cd --- /dev/null +++ b/src/email/parse.rs @@ -0,0 +1,126 @@ +use std::collections::HashMap; + +use serde::Serialize; + +use super::{BodyProperty, Email, Property}; + +#[derive(Debug, Clone, Serialize)] +pub struct EmailParseRequest { + #[serde(rename = "accountId")] + account_id: String, + + #[serde(rename = "blobIds")] + blob_ids: Vec, + + #[serde(rename = "properties")] + properties: Vec, + + #[serde(rename = "bodyProperties")] + body_properties: Vec, + + #[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, +} + +#[derive(Debug, Clone, Serialize)] +pub struct EmailParseResponse { + #[serde(rename = "accountId")] + account_id: String, + + #[serde(rename = "parsed")] + parsed: Option>, + + #[serde(rename = "notParsable")] + not_parsable: Option>, + + #[serde(rename = "notFound")] + not_found: Option>, +} + +impl EmailParseRequest { + pub fn new(account_id: String) -> Self { + EmailParseRequest { + account_id, + blob_ids: Vec::new(), + properties: Vec::new(), + body_properties: Vec::new(), + fetch_text_body_values: false, + fetch_html_body_values: false, + fetch_all_body_values: false, + max_body_value_bytes: 0, + } + } + + pub fn blob_ids(&mut self, blob_ids: U) -> &mut Self + where + U: IntoIterator, + V: Into, + { + self.blob_ids = blob_ids.into_iter().map(|v| v.into()).collect(); + self + } + + pub fn properties(&mut self, properties: impl IntoIterator) -> &mut Self { + self.properties = properties.into_iter().collect(); + self + } + + pub fn body_properties( + &mut self, + body_properties: impl IntoIterator, + ) -> &mut Self { + self.body_properties = 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 EmailParseResponse { + pub fn account_id(&self) -> &str { + &self.account_id + } + + pub fn parsed(&self) -> Option> { + self.parsed.as_ref().map(|map| map.keys()) + } + + pub fn parsed_details(&self, id: &str) -> Option<&Email> { + self.parsed.as_ref().and_then(|map| map.get(id)) + } + + pub fn not_parsable(&self) -> Option<&[String]> { + self.not_parsable.as_deref() + } + + pub fn not_found(&self) -> Option<&[String]> { + self.not_found.as_deref() + } +} diff --git a/src/email/query.rs b/src/email/query.rs new file mode 100644 index 0000000..8fdb417 --- /dev/null +++ b/src/email/query.rs @@ -0,0 +1,282 @@ +use chrono::{DateTime, Utc}; +use serde::Serialize; + +use crate::core::{query, set::from_timestamp}; + +#[derive(Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum Filter { + InMailbox { + #[serde(rename = "inMailbox")] + value: String, + }, + InMailboxOtherThan { + #[serde(rename = "inMailboxOtherThan")] + value: Vec, + }, + Before { + #[serde(rename = "before")] + value: DateTime, + }, + After { + #[serde(rename = "after")] + value: DateTime, + }, + MinSize { + #[serde(rename = "minSize")] + value: u32, + }, + MaxSize { + #[serde(rename = "maxSize")] + value: u32, + }, + AllInThreadHaveKeyword { + #[serde(rename = "allInThreadHaveKeyword")] + value: String, + }, + SomeInThreadHaveKeyword { + #[serde(rename = "someInThreadHaveKeyword")] + value: String, + }, + NoneInThreadHaveKeyword { + #[serde(rename = "noneInThreadHaveKeyword")] + value: String, + }, + HasKeyword { + #[serde(rename = "hasKeyword")] + value: String, + }, + NotKeyword { + #[serde(rename = "notKeyword")] + value: String, + }, + HasAttachment { + #[serde(rename = "hasAttachment")] + value: bool, + }, + Text { + #[serde(rename = "text")] + value: String, + }, + From { + #[serde(rename = "from")] + value: String, + }, + To { + #[serde(rename = "to")] + value: String, + }, + Cc { + #[serde(rename = "cc")] + value: String, + }, + Bcc { + #[serde(rename = "bcc")] + value: String, + }, + Subject { + #[serde(rename = "subject")] + value: String, + }, + Body { + #[serde(rename = "body")] + value: String, + }, + Header { + #[serde(rename = "header")] + value: Vec, + }, +} + +#[derive(Serialize, Debug, Clone)] +#[serde(tag = "property")] +pub enum Comparator { + #[serde(rename = "receivedAt")] + ReceivedAt, + #[serde(rename = "size")] + Size, + #[serde(rename = "from")] + From, + #[serde(rename = "to")] + To, + #[serde(rename = "subject")] + Subject, + #[serde(rename = "sentAt")] + SentAt, + #[serde(rename = "hasKeyword")] + HasKeyword { keyword: String }, + #[serde(rename = "allInThreadHaveKeyword")] + AllInThreadHaveKeyword { keyword: String }, + #[serde(rename = "someInThreadHaveKeyword")] + SomeInThreadHaveKeyword { keyword: String }, +} + +impl Filter { + pub fn in_mailbox(value: impl Into) -> Self { + Filter::InMailbox { + value: value.into(), + } + } + + pub fn in_mailbox_other_than(value: U) -> Self + where + U: IntoIterator, + V: Into, + { + Filter::InMailboxOtherThan { + value: value.into_iter().map(|v| v.into()).collect(), + } + } + + pub fn before(value: i64) -> Self { + Filter::Before { + value: from_timestamp(value), + } + } + + pub fn after(value: i64) -> Self { + Filter::After { + value: from_timestamp(value), + } + } + + pub fn min_size(value: u32) -> Self { + Filter::MinSize { value } + } + + pub fn max_size(value: u32) -> Self { + Filter::MaxSize { value } + } + + pub fn all_in_thread_have_keyword(value: impl Into) -> Self { + Filter::AllInThreadHaveKeyword { + value: value.into(), + } + } + + pub fn some_in_thread_have_keyword(value: impl Into) -> Self { + Filter::SomeInThreadHaveKeyword { + value: value.into(), + } + } + + pub fn none_in_thread_have_keyword(value: impl Into) -> Self { + Filter::NoneInThreadHaveKeyword { + value: value.into(), + } + } + + pub fn has_keyword(value: impl Into) -> Self { + Filter::HasKeyword { + value: value.into(), + } + } + + pub fn not_keyword(value: impl Into) -> Self { + Filter::NotKeyword { + value: value.into(), + } + } + + pub fn has_attachment(value: bool) -> Self { + Filter::HasAttachment { value } + } + + pub fn text(value: impl Into) -> Self { + Filter::Text { + value: value.into(), + } + } + + pub fn from(value: impl Into) -> Self { + Filter::From { + value: value.into(), + } + } + + pub fn to(value: impl Into) -> Self { + Filter::To { + value: value.into(), + } + } + + pub fn cc(value: impl Into) -> Self { + Filter::Cc { + value: value.into(), + } + } + + pub fn bcc(value: impl Into) -> Self { + Filter::Bcc { + value: value.into(), + } + } + + pub fn subject(value: impl Into) -> Self { + Filter::Subject { + value: value.into(), + } + } + + pub fn body(value: impl Into) -> Self { + Filter::Body { + value: value.into(), + } + } + + pub fn header(value: U) -> Self + where + U: IntoIterator, + V: Into, + { + Filter::Header { + value: value.into_iter().map(|v| v.into()).collect(), + } + } +} + +impl Comparator { + pub fn received_at() -> query::Comparator { + query::Comparator::new(Comparator::ReceivedAt) + } + + pub fn size() -> query::Comparator { + query::Comparator::new(Comparator::Size) + } + + pub fn from() -> query::Comparator { + query::Comparator::new(Comparator::From) + } + + pub fn to() -> query::Comparator { + query::Comparator::new(Comparator::To) + } + + pub fn subject() -> query::Comparator { + query::Comparator::new(Comparator::Subject) + } + + pub fn sent_at() -> query::Comparator { + query::Comparator::new(Comparator::SentAt) + } + + pub fn has_keyword(keyword: impl Into) -> query::Comparator { + query::Comparator::new(Comparator::HasKeyword { + keyword: keyword.into(), + }) + } + + pub fn all_in_thread_have_keyword(keyword: impl Into) -> query::Comparator { + query::Comparator::new(Comparator::AllInThreadHaveKeyword { + keyword: keyword.into(), + }) + } + + pub fn some_in_thread_have_keyword( + keyword: impl Into, + ) -> query::Comparator { + query::Comparator::new(Comparator::SomeInThreadHaveKeyword { + keyword: keyword.into(), + }) + } +} diff --git a/src/email/search_snippet.rs b/src/email/search_snippet.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/email/set.rs b/src/email/set.rs index 5469886..c7a6799 100644 --- a/src/email/set.rs +++ b/src/email/set.rs @@ -7,8 +7,12 @@ use super::{ }; impl Email { - pub fn mailbox_ids(mut self, mailbox_ids: impl Iterator) -> Self { - self.mailbox_ids = Some(mailbox_ids.into_iter().map(|s| (s, true)).collect()); + pub fn mailbox_ids(mut self, mailbox_ids: T) -> Self + where + T: IntoIterator, + U: Into, + { + self.mailbox_ids = Some(mailbox_ids.into_iter().map(|s| (s.into(), true)).collect()); self } @@ -21,8 +25,12 @@ impl Email { self } - pub fn keywords(mut self, keywords: impl Iterator) -> Self { - self.keywords = Some(keywords.into_iter().map(|s| (s, true)).collect()); + pub fn keywords(mut self, keywords: T) -> Self + where + T: IntoIterator, + U: Into, + { + self.keywords = Some(keywords.into_iter().map(|s| (s.into(), true)).collect()); self } @@ -33,77 +41,89 @@ impl Email { self } - pub fn message_id(mut self, message_id: impl Iterator) -> Self { - self.message_id = Some(message_id.into_iter().collect()); + pub fn message_id(mut self, message_id: T) -> Self + where + T: IntoIterator, + U: Into, + { + self.message_id = Some(message_id.into_iter().map(|v| v.into()).collect()); self } - pub fn in_reply_to(mut self, in_reply_to: impl Iterator) -> Self { - self.in_reply_to = Some(in_reply_to.into_iter().collect()); + pub fn in_reply_to(mut self, in_reply_to: T) -> Self + where + T: IntoIterator, + U: Into, + { + self.in_reply_to = Some(in_reply_to.into_iter().map(|v| v.into()).collect()); self } - pub fn references(mut self, references: impl Iterator) -> Self { - self.references = Some(references.into_iter().collect()); + pub fn references(mut self, references: T) -> Self + where + T: IntoIterator, + U: Into, + { + self.references = Some(references.into_iter().map(|v| v.into()).collect()); self } pub fn sender(mut self, sender: T) -> Self where - T: Iterator, + T: IntoIterator, U: Into, { - self.sender = Some(sender.map(|s| s.into()).collect()); + self.sender = Some(sender.into_iter().map(|s| s.into()).collect()); self } pub fn from(mut self, from: T) -> Self where - T: Iterator, + T: IntoIterator, U: Into, { - self.from = Some(from.map(|s| s.into()).collect()); + self.from = Some(from.into_iter().map(|s| s.into()).collect()); self } pub fn to(mut self, to: T) -> Self where - T: Iterator, + T: IntoIterator, U: Into, { - self.to = Some(to.map(|s| s.into()).collect()); + self.to = Some(to.into_iter().map(|s| s.into()).collect()); self } pub fn cc(mut self, cc: T) -> Self where - T: Iterator, + T: IntoIterator, U: Into, { - self.cc = Some(cc.map(|s| s.into()).collect()); + self.cc = Some(cc.into_iter().map(|s| s.into()).collect()); self } pub fn bcc(mut self, bcc: T) -> Self where - T: Iterator, + T: IntoIterator, U: Into, { - self.bcc = Some(bcc.map(|s| s.into()).collect()); + self.bcc = Some(bcc.into_iter().map(|s| s.into()).collect()); self } pub fn reply_to(mut self, reply_to: T) -> Self where - T: Iterator, + T: IntoIterator, U: Into, { - self.reply_to = Some(reply_to.map(|s| s.into()).collect()); + self.reply_to = Some(reply_to.into_iter().map(|s| s.into()).collect()); self } - pub fn subject(mut self, subject: String) -> Self { - self.subject = Some(subject); + pub fn subject(mut self, subject: impl Into) -> Self { + self.subject = Some(subject.into()); self } @@ -202,38 +222,42 @@ impl EmailBodyPart { } impl EmailBodyPart { - pub fn part_id(mut self, part_id: String) -> Self { - self.part_id = Some(part_id); + pub fn part_id(mut self, part_id: impl Into) -> Self { + self.part_id = Some(part_id.into()); self } - pub fn blob_id(mut self, blob_id: String) -> Self { - self.blob_id = Some(blob_id); + pub fn blob_id(mut self, blob_id: impl Into) -> Self { + self.blob_id = Some(blob_id.into()); self } - pub fn name(mut self, name: String) -> Self { - self.name = Some(name); + pub fn name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); self } - pub fn content_type(mut self, content_type: String) -> Self { - self.type_ = Some(content_type); + pub fn content_type(mut self, content_type: impl Into) -> Self { + self.type_ = Some(content_type.into()); self } - pub fn content_id(mut self, content_id: String) -> Self { - self.cid = Some(content_id); + pub fn content_id(mut self, content_id: impl Into) -> Self { + self.cid = Some(content_id.into()); self } - pub fn content_language(mut self, content_language: impl Iterator) -> Self { - self.language = Some(content_language.into_iter().collect()); + pub fn content_language(mut self, content_language: T) -> Self + where + T: IntoIterator, + U: Into, + { + self.language = Some(content_language.into_iter().map(|v| v.into()).collect()); self } - pub fn content_location(mut self, content_location: String) -> Self { - self.location = Some(content_location); + pub fn content_location(mut self, content_location: impl Into) -> Self { + self.location = Some(content_location.into()); self } @@ -333,8 +357,8 @@ impl EmailAddressGroup { } impl EmailAddressGroup { - pub fn name(mut self, name: String) -> Self { - self.name = Some(name); + pub fn name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); self } diff --git a/src/email_submission/mod.rs b/src/email_submission/mod.rs index ee95caf..f9728cc 100644 --- a/src/email_submission/mod.rs +++ b/src/email_submission/mod.rs @@ -1,4 +1,5 @@ pub mod get; +pub mod query; pub mod set; use std::collections::HashMap; @@ -6,7 +7,15 @@ use std::collections::HashMap; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; -use crate::Get; +use crate::{email::Email, Get}; + +#[derive(Debug, Clone, Serialize, Default)] +pub struct SetArguments { + #[serde(rename = "onSuccessUpdateEmail")] + on_success_update_email: Option>, + #[serde(rename = "onSuccessDestroyEmail")] + on_success_destroy_email: Option>, +} #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EmailSubmission { @@ -115,7 +124,7 @@ pub enum Displayed { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum EmailSubmissionProperty { +pub enum Property { #[serde(rename = "id")] Id, #[serde(rename = "identityId")] diff --git a/src/email_submission/query.rs b/src/email_submission/query.rs new file mode 100644 index 0000000..2232d3c --- /dev/null +++ b/src/email_submission/query.rs @@ -0,0 +1,108 @@ +use chrono::{DateTime, Utc}; +use serde::Serialize; + +use crate::core::{query, set::from_timestamp}; + +use super::UndoStatus; + +#[derive(Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum Filter { + IdentityIds { + #[serde(rename = "identityIds")] + value: Vec, + }, + EmailIds { + #[serde(rename = "emailIds")] + value: Vec, + }, + ThreadIds { + #[serde(rename = "threadIds")] + value: Vec, + }, + UndoStatus { + #[serde(rename = "undoStatus")] + value: UndoStatus, + }, + Before { + #[serde(rename = "before")] + value: DateTime, + }, + After { + #[serde(rename = "after")] + value: DateTime, + }, +} + +#[derive(Serialize, Debug, Clone)] +#[serde(tag = "property")] +pub enum Comparator { + #[serde(rename = "emailId")] + EmailId, + #[serde(rename = "threadId")] + ThreadId, + #[serde(rename = "sentAt")] + SentAt, +} + +impl Filter { + pub fn identity_ids(value: U) -> Self + where + U: IntoIterator, + V: Into, + { + Filter::IdentityIds { + value: value.into_iter().map(|v| v.into()).collect(), + } + } + + pub fn email_ids(value: U) -> Self + where + U: IntoIterator, + V: Into, + { + Filter::EmailIds { + value: value.into_iter().map(|v| v.into()).collect(), + } + } + + pub fn thread_ids(value: U) -> Self + where + U: IntoIterator, + V: Into, + { + Filter::ThreadIds { + value: value.into_iter().map(|v| v.into()).collect(), + } + } + + pub fn undo_status(value: UndoStatus) -> Self { + Filter::UndoStatus { value } + } + + pub fn before(value: i64) -> Self { + Filter::Before { + value: from_timestamp(value), + } + } + + pub fn after(value: i64) -> Self { + Filter::After { + value: from_timestamp(value), + } + } +} + +impl Comparator { + pub fn email_id() -> query::Comparator { + query::Comparator::new(Comparator::EmailId) + } + + pub fn thread_id() -> query::Comparator { + query::Comparator::new(Comparator::ThreadId) + } + + pub fn sent_at() -> query::Comparator { + query::Comparator::new(Comparator::SentAt) + } +} diff --git a/src/identity/mod.rs b/src/identity/mod.rs index c197be1..38f8271 100644 --- a/src/identity/mod.rs +++ b/src/identity/mod.rs @@ -44,7 +44,7 @@ pub struct Identity { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum IdentityProperty { +pub enum Property { #[serde(rename = "id")] Id, #[serde(rename = "name")] diff --git a/src/lib.rs b/src/lib.rs index d174c89..8b54d68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; pub mod blob; +pub mod client; pub mod core; pub mod email; pub mod email_submission; @@ -29,7 +30,7 @@ pub enum URI { } #[derive(Debug, Clone, Serialize, Deserialize)] -enum Method { +pub enum Method { #[serde(rename = "Core/echo")] Echo, #[serde(rename = "Blob/copy")] diff --git a/src/mailbox/mod.rs b/src/mailbox/mod.rs index 24d5ab8..9d7dbde 100644 --- a/src/mailbox/mod.rs +++ b/src/mailbox/mod.rs @@ -1,4 +1,5 @@ pub mod get; +pub mod query; pub mod set; use crate::core::set::string_not_set; @@ -6,6 +7,20 @@ use crate::mailbox::set::role_not_set; use crate::Get; use serde::{Deserialize, Serialize}; +#[derive(Debug, Clone, Serialize, Default)] +pub struct SetArguments { + #[serde(rename = "onDestroyRemoveEmails")] + on_destroy_remove_emails: bool, +} + +#[derive(Debug, Clone, Serialize, Default)] +pub struct QueryArguments { + #[serde(rename = "sortAsTree")] + sort_as_tree: bool, + #[serde(rename = "filterAsTree")] + filter_as_tree: bool, +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Mailbox { #[serde(skip)] @@ -100,7 +115,7 @@ pub struct MailboxRights { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum MailboxProperty { +pub enum Property { #[serde(rename = "id")] Id, #[serde(rename = "name")] diff --git a/src/mailbox/query.rs b/src/mailbox/query.rs new file mode 100644 index 0000000..5b5c4fb --- /dev/null +++ b/src/mailbox/query.rs @@ -0,0 +1,83 @@ +use serde::Serialize; + +use crate::core::query::{self}; + +use super::Role; + +#[derive(Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum Filter { + ParentId { + #[serde(rename = "parentId")] + value: Option, + }, + Name { + #[serde(rename = "name")] + value: String, + }, + Role { + #[serde(rename = "role")] + value: Option, + }, + HasAnyRole { + #[serde(rename = "hasAnyRole")] + value: bool, + }, + IsSubscribed { + #[serde(rename = "isSubscribed")] + value: bool, + }, +} + +#[derive(Serialize, Debug, Clone)] +#[serde(tag = "property")] +pub enum Comparator { + #[serde(rename = "name")] + Name, + #[serde(rename = "sortOrder")] + SortOrder, + #[serde(rename = "parentId")] + ParentId, +} + +impl Filter { + pub fn parent_id(value: Option) -> Self { + Filter::ParentId { value } + } + + pub fn name(value: String) -> Self { + Filter::Name { value } + } + + pub fn role(value: Role) -> Self { + Filter::Role { + value: if !matches!(value, Role::None) { + value.into() + } else { + None + }, + } + } + + pub fn has_any_role(value: bool) -> Self { + Filter::HasAnyRole { value } + } + + pub fn is_subscribed(value: bool) -> Self { + Filter::IsSubscribed { value } + } +} + +impl Comparator { + pub fn name() -> query::Comparator { + query::Comparator::new(Comparator::Name) + } + + pub fn sort_order() -> query::Comparator { + query::Comparator::new(Comparator::SortOrder) + } + + pub fn parent_id() -> query::Comparator { + query::Comparator::new(Comparator::ParentId) + } +} diff --git a/src/push_subscription/mod.rs b/src/push_subscription/mod.rs index 6b148c2..c863f95 100644 --- a/src/push_subscription/mod.rs +++ b/src/push_subscription/mod.rs @@ -42,7 +42,7 @@ pub struct PushSubscription { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum PushSubscriptionProperty { +pub enum Property { #[serde(rename = "id")] Id, #[serde(rename = "deviceClientId")] diff --git a/src/thread/mod.rs b/src/thread/mod.rs index 162c420..25dc161 100644 --- a/src/thread/mod.rs +++ b/src/thread/mod.rs @@ -8,3 +8,11 @@ pub struct Thread { #[serde(rename = "emailIds")] email_ids: Vec, } + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum Property { + #[serde(rename = "id")] + Id, + #[serde(rename = "emailIds")] + EmailIds, +} diff --git a/src/vacation_response/mod.rs b/src/vacation_response/mod.rs index 70aa76b..b5dcd72 100644 --- a/src/vacation_response/mod.rs +++ b/src/vacation_response/mod.rs @@ -40,3 +40,21 @@ pub struct VacationResponse { #[serde(skip_serializing_if = "string_not_set")] html_body: Option, } + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum Property { + #[serde(rename = "id")] + Id, + #[serde(rename = "isEnabled")] + IsEnabled, + #[serde(rename = "fromDate")] + FromDate, + #[serde(rename = "toDate")] + ToDate, + #[serde(rename = "subject")] + Subject, + #[serde(rename = "textBody")] + TextBody, + #[serde(rename = "htmlBody")] + HtmlBody, +}