From b0ba1737b14d85779ba6c875fa72089426de5ade Mon Sep 17 00:00:00 2001 From: Mauro D Date: Tue, 9 Aug 2022 15:28:55 +0000 Subject: [PATCH] Blocking client and patch support on Principals. --- Cargo.toml | 19 +- src/blob/download.rs | 47 +++ src/blob/helpers_blocking.rs | 34 ++ src/blob/mod.rs | 3 + src/blob/upload.rs | 45 +++ src/client.rs | 156 ++++++++- src/core/request.rs | 23 ++ src/email/helpers_blocking.rs | 381 ++++++++++++++++++++++ src/email/mod.rs | 3 + src/email_submission/helpers_blocking.rs | 208 ++++++++++++ src/email_submission/mod.rs | 3 + src/identity/helpers_blocking.rs | 102 ++++++ src/identity/mod.rs | 3 + src/lib.rs | 1 + src/mailbox/helpers_blocking.rs | 217 ++++++++++++ src/mailbox/mod.rs | 3 + src/principal/get.rs | 4 + src/principal/helpers.rs | 2 +- src/principal/helpers_blocking.rs | 276 ++++++++++++++++ src/principal/mod.rs | 42 ++- src/principal/query.rs | 13 + src/principal/set.rs | 17 +- src/push_subscription/helpers_blocking.rs | 98 ++++++ src/push_subscription/mod.rs | 3 + src/thread/get.rs | 6 + src/thread/helpers.rs | 6 +- src/thread/helpers_blocking.rs | 48 +++ src/thread/mod.rs | 6 +- src/vacation_response/helpers_blocking.rs | 136 ++++++++ src/vacation_response/mod.rs | 3 + 30 files changed, 1883 insertions(+), 25 deletions(-) create mode 100644 src/blob/helpers_blocking.rs create mode 100644 src/email/helpers_blocking.rs create mode 100644 src/email_submission/helpers_blocking.rs create mode 100644 src/identity/helpers_blocking.rs create mode 100644 src/mailbox/helpers_blocking.rs create mode 100644 src/principal/helpers_blocking.rs create mode 100644 src/push_subscription/helpers_blocking.rs create mode 100644 src/thread/helpers_blocking.rs create mode 100644 src/vacation_response/helpers_blocking.rs diff --git a/Cargo.toml b/Cargo.toml index 0570a60..9d5222b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,22 +12,23 @@ categories = ["email"] readme = "README.md" [dependencies] +reqwest = { git = "https://github.com/stalwartlabs/reqwest.git", default-features = false, features = ["rustls-tls"]} +tokio-tungstenite = { version = "0.17", features = ["rustls-tls-webpki-roots"], optional = true} +tokio = { version = "1.16", default-features = false, features = ["io-util"], optional = true } +futures-util = { version = "0.3", optional = true} +async-stream = { version = "0.3", optional = true} serde = { version = "1.0", features = ["derive"]} serde_json = "1.0" chrono = { version = "0.4", features = ["serde"]} -#reqwest = { version = "0.11", default-features = false, features = ["stream", "rustls-tls"]} -reqwest = { git = "https://github.com/stalwartlabs/reqwest.git", default-features = false, features = ["stream", "rustls-tls"]} -futures-util = "0.3" -async-stream = "0.3" -base64 = "0.13" -tokio-tungstenite = { version = "0.17", features = ["rustls-tls-webpki-roots"], optional = true} -tokio = { version = "1.16", default-features = false, features = ["io-util"], optional = true } -parking_lot = "0.12.0" ahash = {version = "0.7.6", features = ["serde"]} +parking_lot = "0.12.0" +base64 = "0.13" [features] -default = [] +default = ["async"] +async = ["futures-util", "async-stream", "reqwest/stream"] websockets = ["tokio", "tokio-tungstenite"] +blocking = ["reqwest/blocking"] debug = [] [profile.bench] diff --git a/src/blob/download.rs b/src/blob/download.rs index 8be148e..a07f19c 100644 --- a/src/blob/download.rs +++ b/src/blob/download.rs @@ -5,6 +5,7 @@ use reqwest::header::CONTENT_TYPE; use crate::{client::Client, core::session::URLPart}; impl Client { + #[cfg(feature = "async")] pub async fn download(&self, blob_id: &str) -> crate::Result> { let account_id = self.default_account_id(); let mut download_url = String::with_capacity( @@ -52,4 +53,50 @@ impl Client { .map(|bytes| bytes.to_vec()) .map_err(|err| err.into()) } + + #[cfg(feature = "blocking")] + pub fn download(&self, blob_id: &str) -> crate::Result> { + let account_id = self.default_account_id(); + let mut download_url = String::with_capacity( + self.session().download_url().len() + account_id.len() + blob_id.len(), + ); + + for part in self.download_url() { + match part { + URLPart::Value(value) => { + download_url.push_str(value); + } + URLPart::Parameter(param) => match param { + super::URLParameter::AccountId => { + download_url.push_str(account_id); + } + super::URLParameter::BlobId => { + download_url.push_str(blob_id); + } + super::URLParameter::Name => { + download_url.push_str("none"); + } + super::URLParameter::Type => { + download_url.push_str("application/octet-stream"); + } + }, + } + } + + let mut headers = self.headers().clone(); + headers.remove(CONTENT_TYPE); + + Client::handle_error( + reqwest::blocking::Client::builder() + .timeout(Duration::from_millis(self.timeout())) + .redirect(self.redirect_policy()) + .default_headers(headers) + .build()? + .get(download_url) + .send()?, + )? + .bytes() + .map(|bytes| bytes.to_vec()) + .map_err(|err| err.into()) + } } diff --git a/src/blob/helpers_blocking.rs b/src/blob/helpers_blocking.rs new file mode 100644 index 0000000..889090d --- /dev/null +++ b/src/blob/helpers_blocking.rs @@ -0,0 +1,34 @@ +use crate::{ + client::Client, + core::request::{Arguments, Request}, + Method, +}; + +use super::copy::{CopyBlobRequest, CopyBlobResponse}; + +impl Client { + pub fn blob_copy( + &self, + from_account_id: impl Into, + blob_id: impl Into, + ) -> crate::Result { + let blob_id = blob_id.into(); + let mut request = self.build(); + request.copy_blob(from_account_id).blob_id(&blob_id); + request.send_single::()?.copied(&blob_id) + } +} + +impl Request<'_> { + pub fn copy_blob(&mut self, from_account_id: impl Into) -> &mut CopyBlobRequest { + self.add_method_call( + Method::CopyBlob, + Arguments::blob_copy(self.params(Method::CopyBlob), from_account_id.into()), + ) + .blob_copy_mut() + } + + pub fn send_copy_blob(self) -> crate::Result { + self.send_single() + } +} diff --git a/src/blob/mod.rs b/src/blob/mod.rs index 620b9ae..0bd1525 100644 --- a/src/blob/mod.rs +++ b/src/blob/mod.rs @@ -2,7 +2,10 @@ use crate::core::session::URLParser; pub mod copy; pub mod download; +#[cfg(feature = "async")] pub mod helpers; +#[cfg(feature = "blocking")] +pub mod helpers_blocking; pub mod upload; pub enum URLParameter { diff --git a/src/blob/upload.rs b/src/blob/upload.rs index 2daa0b8..31936fb 100644 --- a/src/blob/upload.rs +++ b/src/blob/upload.rs @@ -21,6 +21,7 @@ pub struct UploadResponse { } impl Client { + #[cfg(feature = "async")] pub async fn upload( &self, account_id: Option<&str>, @@ -66,6 +67,50 @@ impl Client { ) .map_err(|err| err.into()) } + + #[cfg(feature = "blocking")] + pub fn upload( + &self, + account_id: Option<&str>, + blob: Vec, + content_type: Option<&str>, + ) -> crate::Result { + let account_id = account_id.unwrap_or_else(|| self.default_account_id()); + let mut upload_url = + String::with_capacity(self.session().upload_url().len() + account_id.len()); + + for part in self.upload_url() { + match part { + URLPart::Value(value) => { + upload_url.push_str(value); + } + URLPart::Parameter(param) => { + if let super::URLParameter::AccountId = param { + upload_url.push_str(account_id); + } + } + } + } + + serde_json::from_slice::( + &Client::handle_error( + reqwest::blocking::Client::builder() + .timeout(Duration::from_millis(self.timeout())) + .redirect(self.redirect_policy()) + .default_headers(self.headers().clone()) + .build()? + .post(upload_url) + .header( + CONTENT_TYPE, + content_type.unwrap_or("application/octet-stream"), + ) + .body(blob) + .send()?, + )? + .bytes()?, + ) + .map_err(|err| err.into()) + } } impl UploadResponse { diff --git a/src/client.rs b/src/client.rs index 0bcc58e..f3affb2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -9,7 +9,7 @@ use std::{ use ahash::AHashSet; use reqwest::{ header::{self}, - redirect, Response, + redirect, }; use serde::de::DeserializeOwned; @@ -20,7 +20,7 @@ use crate::{ response, session::{Session, URLPart}, }, - event_source, Error, + Error, }; const DEFAULT_TIMEOUT_MS: u64 = 10 * 1000; @@ -41,7 +41,8 @@ pub struct Client { upload_url: Vec>, download_url: Vec>, - event_source_url: Vec>, + #[cfg(feature = "async")] + event_source_url: Vec>, headers: header::HeaderMap, default_account_id: String, @@ -92,6 +93,7 @@ impl ClientBuilder { self } + #[cfg(feature = "async")] pub async fn connect(self, url: &str) -> crate::Result { let authorization = match self.credentials.expect("Missing credentials") { Credentials::Basic(s) => format!("Basic {}", s), @@ -153,6 +155,84 @@ impl ClientBuilder { Ok(Client { download_url: URLPart::parse(session.download_url())?, upload_url: URLPart::parse(session.upload_url())?, + #[cfg(feature = "async")] + event_source_url: URLPart::parse(session.event_source_url())?, + api_url: session.api_url().to_string(), + session: parking_lot::Mutex::new(Arc::new(session)), + session_url: url.to_string(), + session_updated: true.into(), + trusted_hosts, + #[cfg(feature = "websockets")] + authorization, + timeout: self.timeout, + headers, + default_account_id, + #[cfg(feature = "websockets")] + ws: None.into(), + }) + } + + #[cfg(feature = "blocking")] + pub fn connect(self, url: &str) -> crate::Result { + let authorization = match self.credentials.expect("Missing credentials") { + Credentials::Basic(s) => format!("Basic {}", s), + Credentials::Bearer(s) => format!("Bearer {}", s), + }; + let mut headers = header::HeaderMap::new(); + headers.insert( + header::USER_AGENT, + header::HeaderValue::from_static(USER_AGENT), + ); + headers.insert( + header::AUTHORIZATION, + header::HeaderValue::from_str(&authorization).unwrap(), + ); + + let trusted_hosts = Arc::new(self.trusted_hosts); + + let trusted_hosts_ = trusted_hosts.clone(); + let session: Session = serde_json::from_slice( + &Client::handle_error( + reqwest::blocking::Client::builder() + .timeout(Duration::from_millis(self.timeout)) + .redirect(redirect::Policy::custom(move |attempt| { + if attempt.previous().len() > 5 { + attempt.error("Too many redirects.") + } else if matches!( attempt.url().host_str(), Some(host) if trusted_hosts_.contains(host) ) + { + attempt.follow_trusted() + } else { + let message = format!( + "Aborting redirect request to unknown host '{}'.", + attempt.url().host_str().unwrap_or("") + ); + attempt.error(message) + } + })) + .default_headers(headers.clone()) + .build()? + .get(url) + .send() + ?, + )? + .bytes()?, + )?; + + let default_account_id = session + .primary_accounts() + .next() + .map(|a| a.1.to_string()) + .unwrap_or_default(); + + headers.insert( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ); + + Ok(Client { + download_url: URLPart::parse(session.download_url())?, + upload_url: URLPart::parse(session.upload_url())?, + #[cfg(feature = "async")] event_source_url: URLPart::parse(session.event_source_url())?, api_url: session.api_url().to_string(), session: parking_lot::Mutex::new(Arc::new(session)), @@ -223,6 +303,7 @@ impl Client { }) } + #[cfg(feature = "async")] pub async fn send( &self, request: &request::Request<'_>, @@ -254,6 +335,33 @@ impl Client { Ok(response) } + #[cfg(feature = "blocking")] + pub fn send(&self, request: &request::Request<'_>) -> crate::Result> + where + R: DeserializeOwned, + { + let response: response::Response = serde_json::from_slice( + &Client::handle_error( + reqwest::blocking::Client::builder() + .redirect(self.redirect_policy()) + .timeout(Duration::from_millis(self.timeout)) + .default_headers(self.headers.clone()) + .build()? + .post(&self.api_url) + .body(serde_json::to_string(&request)?) + .send()?, + )? + .bytes()?, + )?; + + if response.session_state() != self.session.lock().state() { + self.session_updated.store(false, Ordering::Relaxed); + } + + Ok(response) + } + + #[cfg(feature = "async")] pub async fn refresh_session(&self) -> crate::Result<()> { let session: Session = serde_json::from_slice( &Client::handle_error( @@ -275,6 +383,25 @@ impl Client { Ok(()) } + #[cfg(feature = "blocking")] + pub async fn refresh_session(&self) -> crate::Result<()> { + let session: Session = serde_json::from_slice( + &Client::handle_error( + reqwest::blocking::Client::builder() + .timeout(Duration::from_millis(DEFAULT_TIMEOUT_MS)) + .redirect(self.redirect_policy()) + .default_headers(self.headers.clone()) + .build()? + .get(&self.session_url) + .send()?, + )? + .bytes()?, + )?; + *self.session.lock() = Arc::new(session); + self.session_updated.store(true, Ordering::Relaxed); + Ok(()) + } + pub fn is_session_updated(&self) -> bool { self.session_updated.load(Ordering::Relaxed) } @@ -300,11 +427,13 @@ impl Client { &self.upload_url } - pub fn event_source_url(&self) -> &[URLPart] { + #[cfg(feature = "async")] + pub fn event_source_url(&self) -> &[URLPart] { &self.event_source_url } - pub async fn handle_error(response: Response) -> crate::Result { + #[cfg(feature = "async")] + pub async fn handle_error(response: reqwest::Response) -> crate::Result { if response.status().is_success() { Ok(response) } else if let Some(b"application/problem+json") = response @@ -319,6 +448,23 @@ impl Client { Err(Error::Server(format!("{}", response.status()))) } } + + #[cfg(feature = "blocking")] + pub fn handle_error( + response: reqwest::blocking::Response, + ) -> crate::Result { + if response.status().is_success() { + Ok(response) + } else if let Some(b"application/problem+json") = response + .headers() + .get(header::CONTENT_TYPE) + .map(|h| h.as_bytes()) + { + Err(Error::Problem(serde_json::from_slice(&response.bytes()?)?)) + } else { + Err(Error::Server(format!("{}", response.status()))) + } + } } impl Credentials { diff --git a/src/core/request.rs b/src/core/request.rs index 3239edf..f313d1d 100644 --- a/src/core/request.rs +++ b/src/core/request.rs @@ -429,15 +429,22 @@ impl<'x> Request<'x> { self } + #[cfg(feature = "async")] pub async fn send(self) -> crate::Result> { self.client.send(&self).await } + #[cfg(feature = "blocking")] + pub fn send(self) -> crate::Result> { + self.client.send(&self) + } + #[cfg(feature = "websockets")] pub async fn send_ws(self) -> crate::Result { self.client.send_ws(self).await } + #[cfg(feature = "async")] pub async fn send_single(self) -> crate::Result where T: DeserializeOwned, @@ -453,6 +460,22 @@ impl<'x> Request<'x> { } } + #[cfg(feature = "blocking")] + pub fn send_single(self) -> crate::Result + where + T: DeserializeOwned, + { + let response: Response> = self.client.send(&self)?; + match response + .unwrap_method_responses() + .pop() + .ok_or_else(|| Error::Internal("Server returned no results".to_string()))? + { + SingleMethodResponse::Ok((_, response, _)) => Ok(response), + SingleMethodResponse::Error((_, err, _)) => Err(err.into()), + } + } + pub fn params(&self, method: Method) -> RequestParams { RequestParams { account_id: self.account_id.clone(), diff --git a/src/email/helpers_blocking.rs b/src/email/helpers_blocking.rs new file mode 100644 index 0000000..f86e36c --- /dev/null +++ b/src/email/helpers_blocking.rs @@ -0,0 +1,381 @@ +use crate::{ + client::Client, + core::{ + changes::{ChangesRequest, ChangesResponse}, + copy::CopyRequest, + get::GetRequest, + query::{Comparator, Filter, QueryRequest, QueryResponse}, + query_changes::{QueryChangesRequest, QueryChangesResponse}, + request::{Arguments, Request}, + response::{EmailCopyResponse, EmailGetResponse, EmailSetResponse}, + set::SetRequest, + }, + Get, Method, Set, +}; + +use super::{ + import::{EmailImportRequest, EmailImportResponse}, + parse::{EmailParseRequest, EmailParseResponse}, + search_snippet::{SearchSnippetGetRequest, SearchSnippetGetResponse}, + BodyProperty, Email, Property, +}; + +impl Client { + pub fn email_import( + &self, + raw_message: Vec, + mailbox_ids: T, + keywords: Option, + received_at: Option, + ) -> crate::Result + where + T: IntoIterator, + U: Into, + V: IntoIterator, + W: Into, + { + self.email_import_account( + self.default_account_id(), + raw_message, + mailbox_ids, + keywords, + received_at, + ) + } + + pub fn email_import_account( + &self, + account_id: &str, + raw_message: Vec, + mailbox_ids: T, + keywords: Option, + received_at: Option, + ) -> crate::Result + where + T: IntoIterator, + U: Into, + V: IntoIterator, + W: Into, + { + let blob_id = self.upload(None, raw_message, None)?.take_blob_id(); + let mut request = self.build(); + let import_request = request + .import_email() + .account_id(account_id) + .email(blob_id) + .mailbox_ids(mailbox_ids); + + if let Some(keywords) = keywords { + import_request.keywords(keywords); + } + + if let Some(received_at) = received_at { + import_request.received_at(received_at); + } + + let id = import_request.create_id(); + request.send_single::()?.created(&id) + } + + pub fn email_set_mailbox( + &self, + id: &str, + mailbox_id: &str, + set: bool, + ) -> crate::Result> { + let mut request = self.build(); + request.set_email().update(id).mailbox_id(mailbox_id, set); + request.send_single::()?.updated(id) + } + + pub fn email_set_mailboxes( + &self, + id: &str, + mailbox_ids: T, + ) -> crate::Result> + where + T: IntoIterator, + U: Into, + { + let mut request = self.build(); + request.set_email().update(id).mailbox_ids(mailbox_ids); + request.send_single::()?.updated(id) + } + + pub fn email_set_keyword( + &self, + id: &str, + keyword: &str, + set: bool, + ) -> crate::Result> { + let mut request = self.build(); + request.set_email().update(id).keyword(keyword, set); + request.send_single::()?.updated(id) + } + + pub fn email_set_keywords(&self, id: &str, keywords: T) -> crate::Result> + where + T: IntoIterator, + U: Into, + { + let mut request = self.build(); + request.set_email().update(id).keywords(keywords); + request.send_single::()?.updated(id) + } + + pub fn email_destroy(&self, id: &str) -> crate::Result<()> { + let mut request = self.build(); + request.set_email().destroy([id]); + request.send_single::()?.destroyed(id) + } + + pub fn email_get( + &self, + id: &str, + properties: Option>, + ) -> crate::Result>> { + let mut request = self.build(); + let get_request = request.get_email().ids([id]); + if let Some(properties) = properties { + get_request.properties(properties); + } + request + .send_single::() + .map(|mut r| r.take_list().pop()) + } + + pub fn email_changes( + &self, + since_state: impl Into, + max_changes: Option, + ) -> crate::Result>> { + let mut request = self.build(); + let changes_request = request.changes_email(since_state); + if let Some(max_changes) = max_changes { + changes_request.max_changes(max_changes); + } + request.send_single() + } + + pub fn email_query( + &self, + filter: Option>>, + sort: Option>>, + ) -> crate::Result { + let mut request = self.build(); + let query_request = request.query_email(); + if let Some(filter) = filter { + query_request.filter(filter); + } + if let Some(sort) = sort { + query_request.sort(sort.into_iter()); + } + request.send_single::() + } + + pub fn email_query_changes( + &self, + since_query_state: impl Into, + filter: Option>>, + ) -> crate::Result { + let mut request = self.build(); + let query_request = request.query_email_changes(since_query_state); + if let Some(filter) = filter { + query_request.filter(filter); + } + request.send_single::() + } + + pub fn email_parse( + &self, + blob_id: &str, + properties: Option>, + body_properties: Option>, + max_body_value_bytes: Option, + ) -> crate::Result { + let mut request = self.build(); + let parse_request = request.parse_email().blob_ids([blob_id]); + if let Some(properties) = properties { + parse_request.properties(properties); + } + + if let Some(body_properties) = body_properties { + parse_request.body_properties(body_properties); + } + + if let Some(max_body_value_bytes) = max_body_value_bytes { + parse_request + .fetch_all_body_values(true) + .max_body_value_bytes(max_body_value_bytes); + } + + request + .send_single::() + .and_then(|mut r| r.parsed(blob_id)) + } + + pub fn email_copy( + &self, + from_account_id: impl Into, + id: impl Into, + mailbox_ids: T, + keywords: Option, + received_at: Option, + ) -> crate::Result + where + T: IntoIterator, + U: Into, + V: IntoIterator, + W: Into, + { + let id = id.into(); + let mut request = self.build(); + let email = request + .copy_email(from_account_id) + .create(id.clone()) + .mailbox_ids(mailbox_ids); + + if let Some(keywords) = keywords { + email.keywords(keywords); + } + + if let Some(received_at) = received_at { + email.received_at(received_at); + } + + request.send_single::()?.created(&id) + } + + pub fn search_snippet_get( + &self, + filter: Option>>, + email_ids: impl IntoIterator>, + ) -> crate::Result { + let mut request = self.build(); + let snippet_request = request.get_search_snippet(); + if let Some(filter) = filter { + snippet_request.filter(filter); + } + snippet_request.email_ids(email_ids); + request.send_single::() + } +} + +impl Request<'_> { + pub fn get_email(&mut self) -> &mut GetRequest> { + self.add_method_call( + Method::GetEmail, + Arguments::email_get(self.params(Method::GetEmail)), + ) + .email_get_mut() + } + + pub fn send_get_email(self) -> crate::Result { + self.send_single() + } + + pub fn changes_email(&mut self, since_state: impl Into) -> &mut ChangesRequest { + self.add_method_call( + Method::ChangesEmail, + Arguments::changes(self.params(Method::ChangesEmail), since_state.into()), + ) + .changes_mut() + } + + pub fn send_changes_email(self) -> crate::Result>> { + self.send_single() + } + + pub fn query_email(&mut self) -> &mut QueryRequest> { + self.add_method_call( + Method::QueryEmail, + Arguments::email_query(self.params(Method::QueryEmail)), + ) + .email_query_mut() + } + + pub fn send_query_email(self) -> crate::Result { + self.send_single() + } + + pub fn query_email_changes( + &mut self, + since_query_state: impl Into, + ) -> &mut QueryChangesRequest> { + self.add_method_call( + Method::QueryChangesEmail, + Arguments::email_query_changes( + self.params(Method::QueryChangesEmail), + since_query_state.into(), + ), + ) + .email_query_changes_mut() + } + + pub fn send_query_email_changes(self) -> crate::Result { + self.send_single() + } + + pub fn set_email(&mut self) -> &mut SetRequest> { + self.add_method_call( + Method::SetEmail, + Arguments::email_set(self.params(Method::SetEmail)), + ) + .email_set_mut() + } + + pub fn send_set_email(self) -> crate::Result { + self.send_single() + } + + pub fn copy_email( + &mut self, + from_account_id: impl Into, + ) -> &mut CopyRequest> { + self.add_method_call( + Method::CopyEmail, + Arguments::email_copy(self.params(Method::CopyEmail), from_account_id.into()), + ) + .email_copy_mut() + } + + pub fn send_copy_email(self) -> crate::Result { + self.send_single() + } + + pub fn import_email(&mut self) -> &mut EmailImportRequest { + self.add_method_call( + Method::ImportEmail, + Arguments::email_import(self.params(Method::ImportEmail)), + ) + .email_import_mut() + } + + pub fn send_import_email(self) -> crate::Result { + self.send_single() + } + + pub fn parse_email(&mut self) -> &mut EmailParseRequest { + self.add_method_call( + Method::ParseEmail, + Arguments::email_parse(self.params(Method::ParseEmail)), + ) + .email_parse_mut() + } + + pub fn send_parse_email(self) -> crate::Result { + self.send_single() + } + + pub fn get_search_snippet(&mut self) -> &mut SearchSnippetGetRequest { + self.add_method_call( + Method::GetSearchSnippet, + Arguments::search_snippet_get(self.params(Method::GetSearchSnippet)), + ) + .search_snippet_get_mut() + } + + pub fn send_get_search_snippet(self) -> crate::Result { + self.send_single() + } +} diff --git a/src/email/mod.rs b/src/email/mod.rs index 74e9b43..3230bcc 100644 --- a/src/email/mod.rs +++ b/src/email/mod.rs @@ -1,5 +1,8 @@ pub mod get; +#[cfg(feature = "async")] pub mod helpers; +#[cfg(feature = "blocking")] +pub mod helpers_blocking; pub mod import; pub mod parse; pub mod query; diff --git a/src/email_submission/helpers_blocking.rs b/src/email_submission/helpers_blocking.rs new file mode 100644 index 0000000..ac14bd3 --- /dev/null +++ b/src/email_submission/helpers_blocking.rs @@ -0,0 +1,208 @@ +use crate::{ + client::Client, + core::{ + changes::{ChangesRequest, ChangesResponse}, + get::GetRequest, + query::{Comparator, Filter, QueryRequest, QueryResponse}, + query_changes::{QueryChangesRequest, QueryChangesResponse}, + request::{Arguments, Request}, + response::{EmailSubmissionGetResponse, EmailSubmissionSetResponse}, + set::{SetObject, SetRequest}, + }, + Get, Method, Set, URI, +}; + +use super::{Address, EmailSubmission, Property, UndoStatus}; + +impl Client { + pub fn email_submission_create( + &self, + email_id: impl Into, + identity_id: impl Into, + ) -> crate::Result> { + let mut request = self.build(); + let id = request + .set_email_submission() + .create() + .email_id(email_id) + .identity_id(identity_id) + .create_id() + .unwrap(); + request + .send_single::()? + .created(&id) + } + + pub fn email_submission_create_envelope( + &self, + email_id: impl Into, + identity_id: impl Into, + mail_from: S, + rcpt_to: T, + ) -> crate::Result> + where + S: Into
, + T: IntoIterator, + U: Into
, + { + let mut request = self.build(); + let id = request + .set_email_submission() + .create() + .email_id(email_id) + .identity_id(identity_id) + .envelope(mail_from, rcpt_to) + .create_id() + .unwrap(); + request + .send_single::()? + .created(&id) + } + + pub fn email_submission_change_status( + &self, + id: &str, + undo_status: UndoStatus, + ) -> crate::Result> { + let mut request = self.build(); + request + .set_email_submission() + .update(id) + .undo_status(undo_status); + request + .send_single::()? + .updated(id) + } + + pub fn email_submission_destroy(&self, id: &str) -> crate::Result<()> { + let mut request = self.build(); + request.set_email_submission().destroy([id]); + request + .send_single::()? + .destroyed(id) + } + + pub fn email_submission_get( + &self, + id: &str, + properties: Option>, + ) -> crate::Result> { + let mut request = self.build(); + let get_request = request.get_email_submission().ids([id]); + if let Some(properties) = properties { + get_request.properties(properties.into_iter()); + } + request + .send_single::() + .map(|mut r| r.take_list().pop()) + } + + pub fn email_submission_query( + &self, + filter: Option>>, + sort: Option>>, + ) -> crate::Result { + let mut request = self.build(); + let query_request = request.query_email_submission(); + if let Some(filter) = filter { + query_request.filter(filter); + } + if let Some(sort) = sort { + query_request.sort(sort.into_iter()); + } + request.send_single::() + } + + pub fn email_submission_changes( + &self, + since_state: impl Into, + max_changes: usize, + ) -> crate::Result>> { + let mut request = self.build(); + request + .changes_email_submission(since_state) + .max_changes(max_changes); + request.send_single() + } +} + +impl Request<'_> { + pub fn get_email_submission(&mut self) -> &mut GetRequest> { + self.add_capability(URI::Submission); + self.add_method_call( + Method::GetEmailSubmission, + Arguments::email_submission_get(self.params(Method::GetEmailSubmission)), + ) + .email_submission_get_mut() + } + + pub fn send_get_email_submission(self) -> crate::Result { + self.send_single() + } + + pub fn changes_email_submission( + &mut self, + since_state: impl Into, + ) -> &mut ChangesRequest { + self.add_capability(URI::Submission); + self.add_method_call( + Method::ChangesEmailSubmission, + Arguments::changes( + self.params(Method::ChangesEmailSubmission), + since_state.into(), + ), + ) + .changes_mut() + } + + pub fn send_changes_email_submission( + self, + ) -> crate::Result>> { + self.send_single() + } + + pub fn query_email_submission(&mut self) -> &mut QueryRequest> { + self.add_capability(URI::Submission); + self.add_method_call( + Method::QueryEmailSubmission, + Arguments::email_submission_query(self.params(Method::QueryEmailSubmission)), + ) + .email_submission_query_mut() + } + + pub fn send_query_email_submission(self) -> crate::Result { + self.send_single() + } + + pub fn query_email_submission_changes( + &mut self, + since_query_state: impl Into, + ) -> &mut QueryChangesRequest> { + self.add_capability(URI::Submission); + self.add_method_call( + Method::QueryChangesEmailSubmission, + Arguments::email_submission_query_changes( + self.params(Method::QueryChangesEmailSubmission), + since_query_state.into(), + ), + ) + .email_submission_query_changes_mut() + } + + pub fn send_query_email_submission_changes(self) -> crate::Result { + self.send_single() + } + + pub fn set_email_submission(&mut self) -> &mut SetRequest> { + self.add_capability(URI::Submission); + self.add_method_call( + Method::SetEmailSubmission, + Arguments::email_submission_set(self.params(Method::SetEmailSubmission)), + ) + .email_submission_set_mut() + } + + pub fn send_set_email_submission(self) -> crate::Result { + self.send_single() + } +} diff --git a/src/email_submission/mod.rs b/src/email_submission/mod.rs index 11028c8..8a849ce 100644 --- a/src/email_submission/mod.rs +++ b/src/email_submission/mod.rs @@ -1,5 +1,8 @@ pub mod get; +#[cfg(feature = "async")] pub mod helpers; +#[cfg(feature = "blocking")] +pub mod helpers_blocking; pub mod query; pub mod set; diff --git a/src/identity/helpers_blocking.rs b/src/identity/helpers_blocking.rs new file mode 100644 index 0000000..9a5592e --- /dev/null +++ b/src/identity/helpers_blocking.rs @@ -0,0 +1,102 @@ +use crate::{ + client::Client, + core::{ + changes::{ChangesRequest, ChangesResponse}, + get::GetRequest, + request::{Arguments, Request}, + response::{IdentityGetResponse, IdentitySetResponse}, + set::{SetObject, SetRequest}, + }, + Get, Method, Set, +}; + +use super::{Identity, Property}; + +impl Client { + pub fn identity_create( + &self, + name: impl Into, + email: impl Into, + ) -> crate::Result { + let mut request = self.build(); + let id = request + .set_identity() + .create() + .name(name) + .email(email) + .create_id() + .unwrap(); + request.send_single::()?.created(&id) + } + + pub fn identity_destroy(&self, id: &str) -> crate::Result<()> { + let mut request = self.build(); + request.set_identity().destroy([id]); + request.send_single::()?.destroyed(id) + } + + pub fn identity_get( + &self, + id: &str, + properties: Option>, + ) -> crate::Result> { + let mut request = self.build(); + let get_request = request.get_identity().ids([id]); + if let Some(properties) = properties { + get_request.properties(properties.into_iter()); + } + request + .send_single::() + .map(|mut r| r.take_list().pop()) + } + + pub fn identity_changes( + &self, + since_state: impl Into, + max_changes: usize, + ) -> crate::Result>> { + let mut request = self.build(); + request + .changes_identity(since_state) + .max_changes(max_changes); + request.send_single() + } +} + +impl Request<'_> { + pub fn get_identity(&mut self) -> &mut GetRequest> { + self.add_method_call( + Method::GetIdentity, + Arguments::identity_get(self.params(Method::GetIdentity)), + ) + .identity_get_mut() + } + + pub fn send_get_identity(self) -> crate::Result { + self.send_single() + } + + pub fn set_identity(&mut self) -> &mut SetRequest> { + self.add_method_call( + Method::SetIdentity, + Arguments::identity_set(self.params(Method::SetIdentity)), + ) + .identity_set_mut() + } + + pub fn send_set_identity(self) -> crate::Result { + self.send_single() + } + + pub fn changes_identity(&mut self, since_state: impl Into) -> &mut ChangesRequest { + self.add_method_call( + Method::ChangesIdentity, + Arguments::changes(self.params(Method::ChangesIdentity), since_state.into()), + ) + .changes_mut() + } + + pub fn send_changes_identity(self) -> crate::Result>> { + self.send_single() + } +} diff --git a/src/identity/mod.rs b/src/identity/mod.rs index f89c7eb..daf8ffd 100644 --- a/src/identity/mod.rs +++ b/src/identity/mod.rs @@ -1,5 +1,8 @@ pub mod get; +#[cfg(feature = "async")] pub mod helpers; +#[cfg(feature = "blocking")] +pub mod helpers_blocking; pub mod set; use std::fmt::Display; diff --git a/src/lib.rs b/src/lib.rs index 8bda37a..71ba62d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ pub mod client; pub mod core; pub mod email; pub mod email_submission; +#[cfg(feature = "async")] pub mod event_source; pub mod identity; pub mod mailbox; diff --git a/src/mailbox/helpers_blocking.rs b/src/mailbox/helpers_blocking.rs new file mode 100644 index 0000000..9da566b --- /dev/null +++ b/src/mailbox/helpers_blocking.rs @@ -0,0 +1,217 @@ +use crate::{ + client::Client, + core::{ + changes::{ChangesRequest, ChangesResponse}, + get::GetRequest, + query::{Comparator, Filter, QueryRequest, QueryResponse}, + query_changes::{QueryChangesRequest, QueryChangesResponse}, + request::{Arguments, Request}, + response::{MailboxGetResponse, MailboxSetResponse}, + set::{SetObject, SetRequest}, + }, + principal::ACL, + Get, Method, Set, +}; + +use super::{Mailbox, Property, Role}; + +impl Client { + pub fn mailbox_create( + &self, + name: impl Into, + parent_id: Option>, + role: Role, + ) -> crate::Result { + let mut request = self.build(); + let id = request + .set_mailbox() + .create() + .name(name) + .role(role) + .parent_id(parent_id) + .create_id() + .unwrap(); + request.send_single::()?.created(&id) + } + + pub fn mailbox_rename( + &self, + id: &str, + name: impl Into, + ) -> crate::Result> { + let mut request = self.build(); + request.set_mailbox().update(id).name(name); + request.send_single::()?.updated(id) + } + + pub fn mailbox_move( + &self, + id: &str, + parent_id: Option>, + ) -> crate::Result> { + let mut request = self.build(); + request.set_mailbox().update(id).parent_id(parent_id); + request.send_single::()?.updated(id) + } + + pub fn mailbox_update_role(&self, id: &str, role: Role) -> crate::Result> { + let mut request = self.build(); + request.set_mailbox().update(id).role(role); + request.send_single::()?.updated(id) + } + + pub fn mailbox_update_acl( + &self, + id: &str, + account_id: &str, + acl: impl IntoIterator, + ) -> crate::Result> { + let mut request = self.build(); + request.set_mailbox().update(id).acl(account_id, acl); + request.send_single::()?.updated(id) + } + + pub fn mailbox_update_sort_order( + &self, + id: &str, + sort_order: u32, + ) -> crate::Result> { + let mut request = self.build(); + request.set_mailbox().update(id).sort_order(sort_order); + request.send_single::()?.updated(id) + } + + pub fn mailbox_subscribe( + &self, + id: &str, + is_subscribed: bool, + ) -> crate::Result> { + let mut request = self.build(); + request + .set_mailbox() + .update(id) + .is_subscribed(is_subscribed); + request.send_single::()?.updated(id) + } + + pub fn mailbox_destroy(&self, id: &str, delete_emails: bool) -> crate::Result<()> { + let mut request = self.build(); + request + .set_mailbox() + .destroy([id]) + .arguments() + .on_destroy_remove_emails(delete_emails); + request.send_single::()?.destroyed(id) + } + + pub fn mailbox_get( + &self, + id: &str, + properties: Option>, + ) -> crate::Result> { + let mut request = self.build(); + let get_request = request.get_mailbox().ids([id]); + if let Some(properties) = properties { + get_request.properties(properties.into_iter()); + } + request + .send_single::() + .map(|mut r| r.take_list().pop()) + } + + pub fn mailbox_query( + &self, + filter: Option>>, + sort: Option>>, + ) -> crate::Result { + let mut request = self.build(); + let query_request = request.query_mailbox(); + if let Some(filter) = filter { + query_request.filter(filter); + } + if let Some(sort) = sort { + query_request.sort(sort.into_iter()); + } + request.send_single::() + } + + pub fn mailbox_changes( + &self, + since_state: impl Into, + max_changes: usize, + ) -> crate::Result>> { + let mut request = self.build(); + request + .changes_mailbox(since_state) + .max_changes(max_changes); + request.send_single() + } +} + +impl Request<'_> { + pub fn get_mailbox(&mut self) -> &mut GetRequest> { + self.add_method_call( + Method::GetMailbox, + Arguments::mailbox_get(self.params(Method::GetMailbox)), + ) + .mailbox_get_mut() + } + + pub fn send_get_mailbox(self) -> crate::Result { + self.send_single() + } + + pub fn changes_mailbox(&mut self, since_state: impl Into) -> &mut ChangesRequest { + self.add_method_call( + Method::ChangesMailbox, + Arguments::changes(self.params(Method::ChangesMailbox), since_state.into()), + ) + .changes_mut() + } + + pub fn send_changes_mailbox(self) -> crate::Result>> { + self.send_single() + } + + pub fn query_mailbox(&mut self) -> &mut QueryRequest> { + self.add_method_call( + Method::QueryMailbox, + Arguments::mailbox_query(self.params(Method::QueryMailbox)), + ) + .mailbox_query_mut() + } + + pub fn send_query_mailbox(self) -> crate::Result { + self.send_single() + } + + pub fn query_mailbox_changes( + &mut self, + since_query_state: impl Into, + ) -> &mut QueryChangesRequest> { + self.add_method_call( + Method::QueryChangesMailbox, + Arguments::mailbox_query_changes( + self.params(Method::QueryChangesMailbox), + since_query_state.into(), + ), + ) + .mailbox_query_changes_mut() + } + + pub fn send_query_mailbox_changes(self) -> crate::Result { + self.send_single() + } + + pub fn set_mailbox(&mut self) -> &mut SetRequest> { + self.add_method_call( + Method::SetMailbox, + Arguments::mailbox_set(self.params(Method::SetMailbox)), + ) + .mailbox_set_mut() + } + + pub fn send_set_mailbox(self) -> crate::Result { + self.send_single() + } +} diff --git a/src/mailbox/mod.rs b/src/mailbox/mod.rs index 76b17ff..90b4033 100644 --- a/src/mailbox/mod.rs +++ b/src/mailbox/mod.rs @@ -1,5 +1,8 @@ pub mod get; +#[cfg(feature = "async")] pub mod helpers; +#[cfg(feature = "blocking")] +pub mod helpers_blocking; pub mod query; pub mod set; diff --git a/src/principal/get.rs b/src/principal/get.rs index 87ccade..bc190c2 100644 --- a/src/principal/get.rs +++ b/src/principal/get.rs @@ -27,6 +27,10 @@ impl Principal { self.description.as_deref() } + pub fn timezone(&self) -> Option<&str> { + self.timezone.as_deref() + } + pub fn secret(&self) -> Option<&str> { self.secret.as_deref() } diff --git a/src/principal/helpers.rs b/src/principal/helpers.rs index cc0f6b7..e9c2879 100644 --- a/src/principal/helpers.rs +++ b/src/principal/helpers.rs @@ -202,7 +202,7 @@ impl Client { pub async fn principal_get( &self, id: &str, - properties: Option>, + properties: Option>, ) -> crate::Result> { let mut request = self.build(); let get_request = request.get_principal().ids([id]); diff --git a/src/principal/helpers_blocking.rs b/src/principal/helpers_blocking.rs new file mode 100644 index 0000000..0691e06 --- /dev/null +++ b/src/principal/helpers_blocking.rs @@ -0,0 +1,276 @@ +use crate::{ + client::Client, + core::{ + changes::{ChangesRequest, ChangesResponse}, + get::GetRequest, + query::{Comparator, Filter, QueryRequest, QueryResponse}, + query_changes::{QueryChangesRequest, QueryChangesResponse}, + request::{Arguments, Request}, + response::{PrincipalGetResponse, PrincipalSetResponse}, + set::{SetObject, SetRequest}, + }, + Get, Method, Set, +}; + +use super::{Principal, Property, Type}; + +impl Client { + pub fn individual_create( + &self, + email: impl Into, + secret: impl Into, + name: impl Into, + ) -> crate::Result { + let mut request = self.build(); + let id = request + .set_principal() + .create() + .name(name) + .secret(secret) + .email(email) + .ptype(Type::Individual) + .create_id() + .unwrap(); + request.send_single::()?.created(&id) + } + + pub fn domain_create(&self, name: impl Into) -> crate::Result { + let mut request = self.build(); + let id = request + .set_principal() + .create() + .name(name) + .ptype(Type::Domain) + .create_id() + .unwrap(); + request.send_single::()?.created(&id) + } + + pub fn list_create( + &self, + email: impl Into, + name: impl Into, + members: impl IntoIterator>, + ) -> crate::Result { + let mut request = self.build(); + let id = request + .set_principal() + .create() + .name(name) + .email(email) + .ptype(Type::List) + .members(members.into()) + .create_id() + .unwrap(); + request.send_single::()?.created(&id) + } + + pub fn group_create( + &self, + email: impl Into, + name: impl Into, + members: impl IntoIterator>, + ) -> crate::Result { + let mut request = self.build(); + let id = request + .set_principal() + .create() + .name(name) + .email(email) + .ptype(Type::Group) + .members(members.into()) + .create_id() + .unwrap(); + request.send_single::()?.created(&id) + } + + pub fn principal_set_name( + &self, + id: &str, + name: impl Into, + ) -> crate::Result> { + let mut request = self.build(); + request.set_principal().update(id).name(name); + request.send_single::()?.updated(id) + } + + pub fn principal_set_secret( + &self, + id: &str, + secret: impl Into, + ) -> crate::Result> { + let mut request = self.build(); + request.set_principal().update(id).secret(secret); + request.send_single::()?.updated(id) + } + + pub fn principal_set_email( + &self, + id: &str, + email: impl Into, + ) -> crate::Result> { + let mut request = self.build(); + request.set_principal().update(id).email(email); + request.send_single::()?.updated(id) + } + + pub fn principal_set_timezone( + &self, + id: &str, + timezone: Option>, + ) -> crate::Result> { + let mut request = self.build(); + request.set_principal().update(id).timezone(timezone); + request.send_single::()?.updated(id) + } + + pub fn principal_set_members( + &self, + id: &str, + members: Option>>, + ) -> crate::Result> { + let mut request = self.build(); + request.set_principal().update(id).members(members); + request.send_single::()?.updated(id) + } + + pub fn principal_set_aliases( + &self, + id: &str, + aliases: Option>>, + ) -> crate::Result> { + let mut request = self.build(); + request.set_principal().update(id).aliases(aliases); + request.send_single::()?.updated(id) + } + + pub fn principal_set_capabilities( + &self, + id: &str, + capabilities: Option>>, + ) -> crate::Result> { + let mut request = self.build(); + request + .set_principal() + .update(id) + .capabilities(capabilities); + request.send_single::()?.updated(id) + } + + pub fn principal_destroy(&self, id: &str) -> crate::Result<()> { + let mut request = self.build(); + request.set_principal().destroy([id]).arguments(); + request.send_single::()?.destroyed(id) + } + + pub fn principal_get( + &self, + id: &str, + properties: Option>, + ) -> crate::Result> { + let mut request = self.build(); + let get_request = request.get_principal().ids([id]); + if let Some(properties) = properties { + get_request.properties(properties.into_iter()); + } + request + .send_single::() + .map(|mut r| r.take_list().pop()) + } + + pub fn principal_query( + &self, + filter: Option>>, + sort: Option>>, + ) -> crate::Result { + let mut request = self.build(); + let query_request = request.query_principal(); + if let Some(filter) = filter { + query_request.filter(filter); + } + if let Some(sort) = sort { + query_request.sort(sort.into_iter()); + } + request.send_single::() + } + + pub fn principal_changes( + &self, + since_state: impl Into, + max_changes: usize, + ) -> crate::Result>> { + let mut request = self.build(); + request + .changes_principal(since_state) + .max_changes(max_changes); + request.send_single() + } +} + +impl Request<'_> { + pub fn get_principal(&mut self) -> &mut GetRequest> { + self.add_method_call( + Method::GetPrincipal, + Arguments::principal_get(self.params(Method::GetPrincipal)), + ) + .principal_get_mut() + } + + pub fn send_get_principal(self) -> crate::Result { + self.send_single() + } + + pub fn changes_principal(&mut self, since_state: impl Into) -> &mut ChangesRequest { + self.add_method_call( + Method::ChangesPrincipal, + Arguments::changes(self.params(Method::ChangesPrincipal), since_state.into()), + ) + .changes_mut() + } + + pub fn send_changes_principal(self) -> crate::Result>> { + self.send_single() + } + + pub fn query_principal(&mut self) -> &mut QueryRequest> { + self.add_method_call( + Method::QueryPrincipal, + Arguments::principal_query(self.params(Method::QueryPrincipal)), + ) + .principal_query_mut() + } + + pub fn send_query_principal(self) -> crate::Result { + self.send_single() + } + + pub fn query_principal_changes( + &mut self, + since_query_state: impl Into, + ) -> &mut QueryChangesRequest> { + self.add_method_call( + Method::QueryChangesPrincipal, + Arguments::principal_query_changes( + self.params(Method::QueryChangesPrincipal), + since_query_state.into(), + ), + ) + .principal_query_changes_mut() + } + + pub fn send_query_principal_changes(self) -> crate::Result { + self.send_single() + } + + pub fn set_principal(&mut self) -> &mut SetRequest> { + self.add_method_call( + Method::SetPrincipal, + Arguments::principal_set(self.params(Method::SetPrincipal)), + ) + .principal_set_mut() + } + + pub fn send_set_principal(self) -> crate::Result { + self.send_single() + } +} diff --git a/src/principal/mod.rs b/src/principal/mod.rs index 4036fc1..dcfc8e2 100644 --- a/src/principal/mod.rs +++ b/src/principal/mod.rs @@ -1,5 +1,8 @@ pub mod get; +#[cfg(feature = "async")] pub mod helpers; +#[cfg(feature = "blocking")] +pub mod helpers_blocking; pub mod query; pub mod set; @@ -23,33 +26,51 @@ pub struct Principal { #[serde(skip_serializing_if = "Option::is_none")] id: Option, + #[serde(rename = "type")] #[serde(skip_serializing_if = "Option::is_none")] ptype: Option, + #[serde(skip_serializing_if = "string_not_set")] name: Option, + #[serde(skip_serializing_if = "string_not_set")] description: Option, + #[serde(skip_serializing_if = "string_not_set")] email: Option, + #[serde(skip_serializing_if = "string_not_set")] timezone: Option, + #[serde(skip_serializing_if = "list_not_set")] capabilities: Option>, + #[serde(skip_serializing_if = "list_not_set")] aliases: Option>, + #[serde(skip_serializing_if = "string_not_set")] secret: Option, + #[serde(skip_serializing_if = "Option::is_none")] dkim: Option, + #[serde(skip_serializing_if = "Option::is_none")] quota: Option, + #[serde(skip_serializing_if = "string_not_set")] picture: Option, + #[serde(skip_serializing_if = "list_not_set")] members: Option>, + #[serde(skip_serializing_if = "map_not_set")] acl: Option>>, + + #[serde(flatten)] + #[serde(skip_deserializing)] + #[serde(skip_serializing_if = "Option::is_none")] + property_patch: Option>, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Copy)] @@ -129,9 +150,26 @@ pub enum Type { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct DKIM { #[serde(rename = "dkimSelector")] - pub dkim_selector: Option, + dkim_selector: Option, #[serde(rename = "dkimExpiration")] - pub dkim_expiration: Option, + dkim_expiration: Option, +} + +impl DKIM { + pub fn new(dkim_selector: Option, dkim_expiration: Option) -> DKIM { + DKIM { + dkim_selector, + dkim_expiration, + } + } + + pub fn selector(&self) -> Option<&str> { + self.dkim_selector.as_deref() + } + + pub fn expiration(&self) -> Option { + self.dkim_expiration + } } impl Display for Property { diff --git a/src/principal/query.rs b/src/principal/query.rs index 8c9e81f..dacd507 100644 --- a/src/principal/query.rs +++ b/src/principal/query.rs @@ -18,6 +18,10 @@ pub enum Filter { #[serde(rename = "name")] value: String, }, + DomainName { + #[serde(rename = "domainName")] + value: String, + }, Text { #[serde(rename = "text")] value: String, @@ -61,16 +65,25 @@ impl Filter { value: value.into(), } } + + pub fn domain_name(value: impl Into) -> Self { + Filter::DomainName { + value: value.into(), + } + } + pub fn email(value: impl Into) -> Self { Filter::Email { value: value.into(), } } + pub fn text(value: impl Into) -> Self { Filter::Text { value: value.into(), } } + pub fn timezone(value: impl Into) -> Self { Filter::Timezone { value: value.into(), diff --git a/src/principal/set.rs b/src/principal/set.rs index 8a08cb4..85aba37 100644 --- a/src/principal/set.rs +++ b/src/principal/set.rs @@ -1,4 +1,4 @@ -use super::{Principal, Type, ACL, DKIM}; +use super::{Principal, Property, Type, ACL, DKIM}; use crate::{core::set::SetObject, Get, Set}; use ahash::AHashMap; @@ -62,6 +62,13 @@ impl Principal { self } + pub fn alias(&mut self, alias: &str, set: bool) -> &mut Self { + self.property_patch + .get_or_insert_with(AHashMap::new) + .insert(format!("{}/{}", Property::Aliases, alias), set); + self + } + pub fn capabilities(&mut self, capabilities: Option) -> &mut Self where T: IntoIterator, @@ -79,6 +86,13 @@ impl Principal { self.members = members.map(|l| l.into_iter().map(|v| v.into()).collect()); self } + + pub fn member(&mut self, member: &str, set: bool) -> &mut Self { + self.property_patch + .get_or_insert_with(AHashMap::new) + .insert(format!("{}/{}", Property::Members, member), set); + self + } } impl SetObject for Principal { @@ -102,6 +116,7 @@ impl SetObject for Principal { picture: "".to_string().into(), members: Vec::with_capacity(0).into(), acl: AHashMap::with_capacity(0).into(), + property_patch: None, } } diff --git a/src/push_subscription/helpers_blocking.rs b/src/push_subscription/helpers_blocking.rs new file mode 100644 index 0000000..6775fe3 --- /dev/null +++ b/src/push_subscription/helpers_blocking.rs @@ -0,0 +1,98 @@ +use crate::{ + client::Client, + core::{ + get::GetRequest, + request::{Arguments, Request}, + response::{PushSubscriptionGetResponse, PushSubscriptionSetResponse}, + set::{SetObject, SetRequest}, + }, + Method, Set, TypeState, +}; + +use super::{Keys, PushSubscription}; + +impl Client { + pub fn push_subscription_create( + &self, + device_client_id: impl Into, + url: impl Into, + keys: Option, + ) -> crate::Result { + let mut request = self.build(); + let create_req = request + .set_push_subscription() + .create() + .device_client_id(device_client_id) + .url(url); + + if let Some(keys) = keys { + create_req.keys(keys); + } + + let id = create_req.create_id().unwrap(); + request + .send_single::()? + .created(&id) + } + + pub fn push_subscription_verify( + &self, + id: &str, + verification_code: impl Into, + ) -> crate::Result> { + let mut request = self.build(); + request + .set_push_subscription() + .update(id) + .verification_code(verification_code); + request + .send_single::()? + .updated(id) + } + + pub fn push_subscription_update_types( + &self, + id: &str, + types: Option>, + ) -> crate::Result> { + let mut request = self.build(); + request.set_push_subscription().update(id).types(types); + request + .send_single::()? + .updated(id) + } + + pub fn push_subscription_destroy(&self, id: &str) -> crate::Result<()> { + let mut request = self.build(); + request.set_push_subscription().destroy([id]); + request + .send_single::()? + .destroyed(id) + } +} + +impl Request<'_> { + pub fn get_push_subscription(&mut self) -> &mut GetRequest> { + self.add_method_call( + Method::GetPushSubscription, + Arguments::push_get(self.params(Method::GetPushSubscription)), + ) + .push_get_mut() + } + + pub fn send_get_push_subscription(self) -> crate::Result { + self.send_single() + } + + pub fn set_push_subscription(&mut self) -> &mut SetRequest> { + self.add_method_call( + Method::SetPushSubscription, + Arguments::push_set(self.params(Method::SetPushSubscription)), + ) + .push_set_mut() + } + + pub fn send_set_push_subscription(self) -> crate::Result { + self.send_single() + } +} diff --git a/src/push_subscription/mod.rs b/src/push_subscription/mod.rs index 1408ea2..d0e2cd5 100644 --- a/src/push_subscription/mod.rs +++ b/src/push_subscription/mod.rs @@ -1,5 +1,8 @@ pub mod get; +#[cfg(feature = "async")] pub mod helpers; +#[cfg(feature = "blocking")] +pub mod helpers_blocking; pub mod set; use std::fmt::Display; diff --git a/src/thread/get.rs b/src/thread/get.rs index 8e1c627..209a248 100644 --- a/src/thread/get.rs +++ b/src/thread/get.rs @@ -1,3 +1,5 @@ +use crate::core::get::GetObject; + use super::Thread; impl Thread { @@ -9,3 +11,7 @@ impl Thread { &self.email_ids } } + +impl GetObject for Thread { + type GetArguments = (); +} diff --git a/src/thread/helpers.rs b/src/thread/helpers.rs index 330f3b6..eddf684 100644 --- a/src/thread/helpers.rs +++ b/src/thread/helpers.rs @@ -2,7 +2,7 @@ use crate::{ client::Client, core::{ changes::{ChangesRequest, ChangesResponse}, - get::{GetObject, GetRequest}, + get::GetRequest, request::{Arguments, Request}, response::ThreadGetResponse, }, @@ -47,7 +47,3 @@ impl Request<'_> { self.send_single().await } } - -impl GetObject for Thread { - type GetArguments = (); -} diff --git a/src/thread/helpers_blocking.rs b/src/thread/helpers_blocking.rs new file mode 100644 index 0000000..bf5a2e4 --- /dev/null +++ b/src/thread/helpers_blocking.rs @@ -0,0 +1,48 @@ +use crate::{ + client::Client, + core::{ + changes::{ChangesRequest, ChangesResponse}, + get::GetRequest, + request::{Arguments, Request}, + response::ThreadGetResponse, + }, + Method, +}; + +use super::Thread; + +impl Client { + pub fn thread_get(&self, id: &str) -> crate::Result> { + let mut request = self.build(); + request.get_thread().ids([id]); + request + .send_single::() + .map(|mut r| r.take_list().pop()) + } +} + +impl Request<'_> { + pub fn get_thread(&mut self) -> &mut GetRequest { + self.add_method_call( + Method::GetThread, + Arguments::thread_get(self.params(Method::GetThread)), + ) + .thread_get_mut() + } + + pub fn send_get_thread(self) -> crate::Result { + self.send_single() + } + + pub fn changes_thread(&mut self, since_state: impl Into) -> &mut ChangesRequest { + self.add_method_call( + Method::ChangesThread, + Arguments::changes(self.params(Method::ChangesThread), since_state.into()), + ) + .changes_mut() + } + + pub fn send_changes_thread(self) -> crate::Result> { + self.send_single() + } +} diff --git a/src/thread/mod.rs b/src/thread/mod.rs index 4427ce1..ebbf4b2 100644 --- a/src/thread/mod.rs +++ b/src/thread/mod.rs @@ -1,11 +1,14 @@ pub mod get; +#[cfg(feature = "async")] pub mod helpers; +#[cfg(feature = "blocking")] +pub mod helpers_blocking; use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::core::{Object, changes::ChangesObject}; +use crate::core::{changes::ChangesObject, Object}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Thread { @@ -42,4 +45,3 @@ impl Display for Property { impl ChangesObject for Thread { type ChangesResponse = (); } - diff --git a/src/vacation_response/helpers_blocking.rs b/src/vacation_response/helpers_blocking.rs new file mode 100644 index 0000000..e3efba1 --- /dev/null +++ b/src/vacation_response/helpers_blocking.rs @@ -0,0 +1,136 @@ +use crate::{ + client::Client, + core::{ + get::GetRequest, + request::{Arguments, Request}, + response::{VacationResponseGetResponse, VacationResponseSetResponse}, + set::{SetObject, SetRequest}, + }, + Method, Set, URI, +}; + +use super::{Property, VacationResponse}; + +impl Client { + pub fn vacation_response_create( + &self, + subject: impl Into, + text_body: Option>, + html_body: Option>, + ) -> crate::Result { + let mut request = self.build(); + let created_id = request + .set_vacation_response() + .create() + .is_enabled(true) + .subject(Some(subject)) + .text_body(text_body) + .html_body(html_body) + .create_id() + .unwrap(); + + request + .send_single::()? + .created(&created_id) + } + + pub fn vacation_response_enable( + &self, + subject: impl Into, + text_body: Option>, + html_body: Option>, + ) -> crate::Result> { + let mut request = self.build(); + request + .set_vacation_response() + .update("singleton") + .is_enabled(true) + .subject(Some(subject)) + .text_body(text_body) + .html_body(html_body); + + request + .send_single::()? + .updated("singleton") + } + + pub fn vacation_response_disable(&self) -> crate::Result> { + let mut request = self.build(); + request + .set_vacation_response() + .update("singleton") + .is_enabled(false); + + request + .send_single::()? + .updated("singleton") + } + + pub fn vacation_response_set_dates( + &self, + from_date: Option, + to_date: Option, + ) -> crate::Result> { + let mut request = self.build(); + request + .set_vacation_response() + .update("singleton") + .is_enabled(true) + .from_date(from_date) + .to_date(to_date); + + request + .send_single::()? + .updated("singleton") + } + + pub fn vacation_response_get( + &self, + properties: Option>, + ) -> crate::Result> { + let mut request = self.build(); + let get_request = request.get_vacation_response().ids(["singleton"]); + if let Some(properties) = properties { + get_request.properties(properties.into_iter()); + } + request + .send_single::() + .map(|mut r| r.take_list().pop()) + } + + pub fn vacation_response_destroy(&self) -> crate::Result<()> { + let mut request = self.build(); + request.set_vacation_response().destroy(["singleton"]); + request + .send_single::()? + .destroyed("singleton") + } +} + +impl Request<'_> { + pub fn get_vacation_response(&mut self) -> &mut GetRequest> { + self.add_capability(URI::VacationResponse); + self.add_method_call( + Method::GetVacationResponse, + Arguments::vacation_response_get(self.params(Method::GetVacationResponse)), + ) + .vacation_response_get_mut() + } + + pub fn send_get_vacation_response(self) -> crate::Result { + self.send_single() + } + + pub fn set_vacation_response(&mut self) -> &mut SetRequest> { + self.add_capability(URI::VacationResponse); + self.add_method_call( + Method::SetVacationResponse, + Arguments::vacation_response_set(self.params(Method::GetVacationResponse)), + ) + .vacation_response_set_mut() + } + + pub fn send_set_vacation_response(self) -> crate::Result { + self.send_single() + } +} diff --git a/src/vacation_response/mod.rs b/src/vacation_response/mod.rs index 51b1e23..d6ac7ea 100644 --- a/src/vacation_response/mod.rs +++ b/src/vacation_response/mod.rs @@ -1,5 +1,8 @@ pub mod get; +#[cfg(feature = "async")] pub mod helpers; +#[cfg(feature = "blocking")] +pub mod helpers_blocking; pub mod set; use std::fmt::Display;