From c085cf5c89f79b6ec81657e14a589760111311ff Mon Sep 17 00:00:00 2001 From: Mauro D Date: Fri, 17 Jun 2022 17:31:00 +0000 Subject: [PATCH] Non-mutable client. --- src/client.rs | 75 ++++++++++++++++++-------------- src/client_ws.rs | 34 ++++++++++----- src/core/error.rs | 40 ++++++++++------- src/core/request.rs | 19 ++++---- src/email/helpers.rs | 22 +++++----- src/email_submission/helpers.rs | 14 +++--- src/identity/helpers.rs | 8 ++-- src/mailbox/helpers.rs | 18 ++++---- src/principal/helpers.rs | 30 ++++++------- src/push_subscription/helpers.rs | 8 ++-- src/thread/helpers.rs | 2 +- src/vacation_response/helpers.rs | 12 ++--- 12 files changed, 155 insertions(+), 127 deletions(-) diff --git a/src/client.rs b/src/client.rs index 1e30bb6..079ea1d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,4 +1,7 @@ -use std::time::Duration; +use std::{ + sync::atomic::{AtomicBool, Ordering}, + time::Duration, +}; use reqwest::{ header::{self}, @@ -27,6 +30,7 @@ pub enum Credentials { pub struct Client { session: Session, session_url: String, + session_outdated: AtomicBool, #[cfg(feature = "websockets")] pub(crate) authorization: String, upload_url: Vec>, @@ -36,7 +40,7 @@ pub struct Client { headers: header::HeaderMap, default_account_id: String, #[cfg(feature = "websockets")] - ws: Option, + pub(crate) ws: tokio::sync::Mutex>, } impl Client { @@ -87,13 +91,14 @@ impl Client { event_source_url: URLPart::parse(session.event_source_url())?, session, session_url: url.to_string(), + session_outdated: false.into(), #[cfg(feature = "websockets")] authorization, timeout: DEFAULT_TIMEOUT_MS, headers, default_account_id, #[cfg(feature = "websockets")] - ws: None, + ws: None.into(), }) } @@ -110,12 +115,16 @@ impl Client { &self.session } + pub fn session_url(&self) -> &str { + &self.session_url + } + pub fn headers(&self) -> &header::HeaderMap { &self.headers } pub async fn send( - &mut self, + &self, request: &request::Request<'_>, ) -> crate::Result> where @@ -138,29 +147,39 @@ impl Client { )?; if response.session_state() != self.session.state() { - let session: Session = serde_json::from_slice( - &Client::handle_error( - reqwest::Client::builder() - .timeout(Duration::from_millis(DEFAULT_TIMEOUT_MS)) - .default_headers(self.headers.clone()) - .build()? - .get(&self.session_url) - .send() - .await?, - ) - .await? - .bytes() - .await?, - )?; - self.download_url = URLPart::parse(session.download_url())?; - self.upload_url = URLPart::parse(session.upload_url())?; - self.event_source_url = URLPart::parse(session.event_source_url())?; - self.session = session; + self.session_outdated.store(true, Ordering::Relaxed); } Ok(response) } + pub async fn refresh_session(&mut self) -> crate::Result<()> { + let session: Session = serde_json::from_slice( + &Client::handle_error( + reqwest::Client::builder() + .timeout(Duration::from_millis(DEFAULT_TIMEOUT_MS)) + .default_headers(self.headers.clone()) + .build()? + .get(&self.session_url) + .send() + .await?, + ) + .await? + .bytes() + .await?, + )?; + self.download_url = URLPart::parse(session.download_url())?; + self.upload_url = URLPart::parse(session.upload_url())?; + self.event_source_url = URLPart::parse(session.event_source_url())?; + self.session = session; + self.session_outdated.store(false, Ordering::Relaxed); + Ok(()) + } + + pub fn is_session_updated(&self) -> bool { + !self.session_outdated.load(Ordering::Relaxed) + } + pub fn set_default_account_id(&mut self, defaul_account_id: impl Into) -> &mut Self { self.default_account_id = defaul_account_id.into(); self @@ -170,7 +189,7 @@ impl Client { &self.default_account_id } - pub fn build(&mut self) -> Request<'_> { + pub fn build(&self) -> Request<'_> { Request::new(self) } @@ -201,16 +220,6 @@ impl Client { Err(Error::Server(format!("{}", response.status()))) } } - - #[cfg(feature = "websockets")] - pub fn set_ws_stream(&mut self, ws: crate::client_ws::WsStream) { - self.ws = Some(ws); - } - - #[cfg(feature = "websockets")] - pub fn ws_stream(&mut self) -> Option<&mut crate::client_ws::WsStream> { - self.ws.as_mut() - } } impl Credentials { diff --git a/src/client_ws.rs b/src/client_ws.rs index b4895fc..237e21c 100644 --- a/src/client_ws.rs +++ b/src/client_ws.rs @@ -123,7 +123,7 @@ pub struct WebSocketProblem { status: Option, title: Option, detail: Option, - limit: Option, + limit: Option, } #[derive(Serialize, Deserialize, Debug)] @@ -152,7 +152,7 @@ pub struct WsStream { impl Client { pub async fn connect_ws( - &mut self, + &self, ) -> crate::Result>>>> { let capabilities = self.session().websocket_capabilities().ok_or_else(|| { crate::Error::Internal( @@ -168,7 +168,7 @@ impl Client { let (stream, _) = tokio_tungstenite::connect_async(request).await?; let (tx, mut rx) = stream.split(); - self.set_ws_stream(WsStream { tx, req_id: 0 }); + *self.ws.lock().await = WsStream { tx, req_id: 0 }.into(); Ok(Box::pin(async_stream::stream! { while let Some(message) = rx.next().await { @@ -202,9 +202,10 @@ impl Client { })) } - pub async fn send_ws(&mut self, request: Request<'_>) -> crate::Result { - let ws = self - .ws_stream() + pub async fn send_ws(&self, request: Request<'_>) -> crate::Result { + let mut _ws = self.ws.lock().await; + let ws = _ws + .as_mut() .ok_or_else(|| crate::Error::Internal("Websocket stream not set.".to_string()))?; // Assing request id @@ -228,11 +229,14 @@ impl Client { } pub async fn enable_push_ws( - &mut self, + &self, data_types: Option>, push_state: Option>, ) -> crate::Result<()> { - self.ws_stream() + self.ws + .lock() + .await + .as_mut() .ok_or_else(|| crate::Error::Internal("Websocket stream not set.".to_string()))? .tx .send(Message::text( @@ -247,8 +251,11 @@ impl Client { .map_err(|err| err.into()) } - pub async fn disable_push_ws(&mut self) -> crate::Result<()> { - self.ws_stream() + pub async fn disable_push_ws(&self) -> crate::Result<()> { + self.ws + .lock() + .await + .as_mut() .ok_or_else(|| crate::Error::Internal("Websocket stream not set.".to_string()))? .tx .send(Message::text( @@ -261,8 +268,11 @@ impl Client { .map_err(|err| err.into()) } - pub async fn ws_ping(&mut self) -> crate::Result<()> { - self.ws_stream() + pub async fn ws_ping(&self) -> crate::Result<()> { + self.ws + .lock() + .await + .as_mut() .ok_or_else(|| crate::Error::Internal("Websocket stream not set.".to_string()))? .tx .send(Message::Ping(vec![])) diff --git a/src/core/error.rs b/src/core/error.rs index 192fd8b..bca59af 100644 --- a/src/core/error.rs +++ b/src/core/error.rs @@ -6,15 +6,15 @@ use serde::Deserialize; pub struct ProblemDetails { #[serde(rename = "type")] p_type: ProblemType, - status: Option, + pub status: Option, title: Option, detail: Option, - limit: Option, + limit: Option, request_id: Option, } #[derive(Debug, Deserialize)] -pub enum ProblemType { +pub enum JMAPError { #[serde(rename = "urn:ietf:params:jmap:error:unknownCapability")] UnknownCapability, #[serde(rename = "urn:ietf:params:jmap:error:notJSON")] @@ -26,12 +26,19 @@ pub enum ProblemType { } #[derive(Debug, Deserialize)] -pub struct MethodError { - #[serde(rename = "type")] - p_type: MethodErrorType, +#[serde(untagged)] +pub enum ProblemType { + JMAP(JMAPError), + Other(String), } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, PartialEq, Eq)] +pub struct MethodError { + #[serde(rename = "type")] + pub p_type: MethodErrorType, +} + +#[derive(Debug, Deserialize, PartialEq, Eq)] pub enum MethodErrorType { #[serde(rename = "serverUnavailable")] ServerUnavailable, @@ -81,7 +88,7 @@ impl ProblemDetails { status: Option, title: Option, detail: Option, - limit: Option, + limit: Option, request_id: Option, ) -> Self { ProblemDetails { @@ -110,8 +117,8 @@ impl ProblemDetails { self.detail.as_deref() } - pub fn limit(&self) -> Option { - self.limit + pub fn limit(&self) -> Option<&str> { + self.limit.as_deref() } pub fn request_id(&self) -> Option<&str> { @@ -158,11 +165,14 @@ impl Display for MethodError { impl Display for ProblemDetails { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.p_type { - ProblemType::UnknownCapability => write!(f, "Unknown capability")?, - ProblemType::NotJSON => write!(f, "Not JSON")?, - ProblemType::NotRequest => write!(f, "Not request")?, - ProblemType::Limit => write!(f, "Limit")?, + match &self.p_type { + ProblemType::JMAP(err) => match err { + JMAPError::UnknownCapability => write!(f, "Unknown capability")?, + JMAPError::NotJSON => write!(f, "Not JSON")?, + JMAPError::NotRequest => write!(f, "Not request")?, + JMAPError::Limit => write!(f, "Limit")?, + }, + ProblemType::Other(err) => f.write_str(err.as_str())?, } if let Some(status) = self.status { diff --git a/src/core/request.rs b/src/core/request.rs index b36af3d..37e478c 100644 --- a/src/core/request.rs +++ b/src/core/request.rs @@ -30,7 +30,7 @@ use super::{ #[derive(Serialize)] pub struct Request<'x> { #[serde(skip)] - client: Option<&'x mut Client>, + client: &'x Client, #[serde(skip)] default_account_id: String, @@ -401,31 +401,30 @@ impl Arguments { } impl<'x> Request<'x> { - pub fn new(client: &'x mut Client) -> Self { + pub fn new(client: &'x Client) -> Self { Request { using: vec![URI::Core, URI::Mail], method_calls: vec![], created_ids: None, default_account_id: client.default_account_id().to_string(), - client: client.into(), + client, } } - pub async fn send(mut self) -> crate::Result> { - Option::take(&mut self.client).unwrap().send(&self).await + pub async fn send(self) -> crate::Result> { + self.client.send(&self).await } #[cfg(feature = "websockets")] - pub async fn send_ws(mut self) -> crate::Result { - Option::take(&mut self.client).unwrap().send_ws(self).await + pub async fn send_ws(self) -> crate::Result { + self.client.send_ws(self).await } - pub async fn send_single(mut self) -> crate::Result + pub async fn send_single(self) -> crate::Result where T: DeserializeOwned, { - let response: Response> = - Option::take(&mut self.client).unwrap().send(&self).await?; + let response: Response> = self.client.send(&self).await?; match response .unwrap_method_responses() .pop() diff --git a/src/email/helpers.rs b/src/email/helpers.rs index 7932c03..02c35e4 100644 --- a/src/email/helpers.rs +++ b/src/email/helpers.rs @@ -21,7 +21,7 @@ use super::{ impl Client { pub async fn email_import( - &mut self, + &self, raw_message: Vec, mailbox_ids: T, keywords: Option, @@ -56,7 +56,7 @@ impl Client { } pub async fn email_set_mailbox( - &mut self, + &self, id: &str, mailbox_id: &str, set: bool, @@ -67,7 +67,7 @@ impl Client { } pub async fn email_set_mailboxes( - &mut self, + &self, id: &str, mailbox_ids: T, ) -> crate::Result> @@ -81,7 +81,7 @@ impl Client { } pub async fn email_set_keyword( - &mut self, + &self, id: &str, keyword: &str, set: bool, @@ -92,7 +92,7 @@ impl Client { } pub async fn email_set_keywords( - &mut self, + &self, id: &str, keywords: T, ) -> crate::Result> @@ -105,7 +105,7 @@ impl Client { request.send_single::().await?.updated(id) } - pub async fn email_destroy(&mut self, id: &str) -> crate::Result<()> { + pub async fn email_destroy(&self, id: &str) -> crate::Result<()> { let mut request = self.build(); request.set_email().destroy([id]); request @@ -115,7 +115,7 @@ impl Client { } pub async fn email_get( - &mut self, + &self, id: &str, properties: Option>, ) -> crate::Result>> { @@ -131,7 +131,7 @@ impl Client { } pub async fn email_changes( - &mut self, + &self, since_state: impl Into, max_changes: usize, ) -> crate::Result>> { @@ -141,7 +141,7 @@ impl Client { } pub async fn email_query( - &mut self, + &self, filter: Option>>, sort: Option>>, ) -> crate::Result { @@ -157,7 +157,7 @@ impl Client { } pub async fn email_parse( - &mut self, + &self, blob_id: &str, properties: Option>, body_properties: Option>, @@ -186,7 +186,7 @@ impl Client { } pub async fn email_copy( - &mut self, + &self, from_account_id: impl Into, id: impl Into, mailbox_ids: T, diff --git a/src/email_submission/helpers.rs b/src/email_submission/helpers.rs index acf0329..fd0bde8 100644 --- a/src/email_submission/helpers.rs +++ b/src/email_submission/helpers.rs @@ -16,7 +16,7 @@ use super::{Address, EmailSubmission, Property, UndoStatus}; impl Client { pub async fn email_submission_create( - &mut self, + &self, email_id: impl Into, identity_id: impl Into, ) -> crate::Result> { @@ -35,7 +35,7 @@ impl Client { } pub async fn email_submission_create_envelope( - &mut self, + &self, email_id: impl Into, identity_id: impl Into, mail_from: S, @@ -62,7 +62,7 @@ impl Client { } pub async fn email_submission_change_status( - &mut self, + &self, id: &str, undo_status: UndoStatus, ) -> crate::Result> { @@ -77,7 +77,7 @@ impl Client { .updated(id) } - pub async fn email_submission_destroy(&mut self, id: &str) -> crate::Result<()> { + pub async fn email_submission_destroy(&self, id: &str) -> crate::Result<()> { let mut request = self.build(); request.set_email_submission().destroy([id]); request @@ -87,7 +87,7 @@ impl Client { } pub async fn email_submission_get( - &mut self, + &self, id: &str, properties: Option>, ) -> crate::Result> { @@ -103,7 +103,7 @@ impl Client { } pub async fn email_submission_query( - &mut self, + &self, filter: Option>>, sort: Option>>, ) -> crate::Result { @@ -119,7 +119,7 @@ impl Client { } pub async fn email_submission_changes( - &mut self, + &self, since_state: impl Into, max_changes: usize, ) -> crate::Result>> { diff --git a/src/identity/helpers.rs b/src/identity/helpers.rs index e3f9802..62fe542 100644 --- a/src/identity/helpers.rs +++ b/src/identity/helpers.rs @@ -14,7 +14,7 @@ use super::{Identity, Property}; impl Client { pub async fn identity_create( - &mut self, + &self, name: impl Into, email: impl Into, ) -> crate::Result { @@ -32,7 +32,7 @@ impl Client { .created(&id) } - pub async fn identity_destroy(&mut self, id: &str) -> crate::Result<()> { + pub async fn identity_destroy(&self, id: &str) -> crate::Result<()> { let mut request = self.build(); request.set_identity().destroy([id]); request @@ -42,7 +42,7 @@ impl Client { } pub async fn identity_get( - &mut self, + &self, id: &str, properties: Option>, ) -> crate::Result> { @@ -58,7 +58,7 @@ impl Client { } pub async fn identity_changes( - &mut self, + &self, since_state: impl Into, max_changes: usize, ) -> crate::Result>> { diff --git a/src/mailbox/helpers.rs b/src/mailbox/helpers.rs index 6de05cf..2b0db85 100644 --- a/src/mailbox/helpers.rs +++ b/src/mailbox/helpers.rs @@ -16,7 +16,7 @@ use super::{Mailbox, Property, Role}; impl Client { pub async fn mailbox_create( - &mut self, + &self, name: impl Into, parent_id: Option>, role: Role, @@ -37,7 +37,7 @@ impl Client { } pub async fn mailbox_rename( - &mut self, + &self, id: &str, name: impl Into, ) -> crate::Result> { @@ -50,7 +50,7 @@ impl Client { } pub async fn mailbox_move( - &mut self, + &self, id: &str, parent_id: Option>, ) -> crate::Result> { @@ -63,7 +63,7 @@ impl Client { } pub async fn mailbox_update_role( - &mut self, + &self, id: &str, role: Role, ) -> crate::Result> { @@ -76,7 +76,7 @@ impl Client { } pub async fn mailbox_update_sort_order( - &mut self, + &self, id: &str, sort_order: u32, ) -> crate::Result> { @@ -88,7 +88,7 @@ impl Client { .updated(id) } - pub async fn mailbox_destroy(&mut self, id: &str, delete_emails: bool) -> crate::Result<()> { + pub async fn mailbox_destroy(&self, id: &str, delete_emails: bool) -> crate::Result<()> { let mut request = self.build(); request .set_mailbox() @@ -102,7 +102,7 @@ impl Client { } pub async fn mailbox_get( - &mut self, + &self, id: &str, properties: Option>, ) -> crate::Result> { @@ -118,7 +118,7 @@ impl Client { } pub async fn mailbox_query( - &mut self, + &self, filter: Option>>, sort: Option>>, ) -> crate::Result { @@ -134,7 +134,7 @@ impl Client { } pub async fn mailbox_changes( - &mut self, + &self, since_state: impl Into, max_changes: usize, ) -> crate::Result>> { diff --git a/src/principal/helpers.rs b/src/principal/helpers.rs index d3653d0..0999245 100644 --- a/src/principal/helpers.rs +++ b/src/principal/helpers.rs @@ -16,7 +16,7 @@ use super::{Principal, Property, Type}; impl Client { pub async fn individual_create( - &mut self, + &self, email: impl Into, secret: impl Into, name: impl Into, @@ -37,7 +37,7 @@ impl Client { .created(&id) } - pub async fn domain_create(&mut self, name: impl Into) -> crate::Result { + pub async fn domain_create(&self, name: impl Into) -> crate::Result { let mut request = self.build(); let id = request .set_principal() @@ -53,7 +53,7 @@ impl Client { } pub async fn list_create( - &mut self, + &self, email: impl Into, name: impl Into, members: impl IntoIterator>, @@ -75,7 +75,7 @@ impl Client { } pub async fn group_create( - &mut self, + &self, email: impl Into, name: impl Into, members: impl IntoIterator>, @@ -97,7 +97,7 @@ impl Client { } pub async fn principal_set_name( - &mut self, + &self, id: &str, name: impl Into, ) -> crate::Result> { @@ -110,7 +110,7 @@ impl Client { } pub async fn principal_set_secret( - &mut self, + &self, id: &str, secret: impl Into, ) -> crate::Result> { @@ -123,7 +123,7 @@ impl Client { } pub async fn principal_set_email( - &mut self, + &self, id: &str, email: impl Into, ) -> crate::Result> { @@ -136,7 +136,7 @@ impl Client { } pub async fn principal_set_timezone( - &mut self, + &self, id: &str, timezone: Option>, ) -> crate::Result> { @@ -149,7 +149,7 @@ impl Client { } pub async fn principal_set_members( - &mut self, + &self, id: &str, members: Option>>, ) -> crate::Result> { @@ -162,7 +162,7 @@ impl Client { } pub async fn principal_set_aliases( - &mut self, + &self, id: &str, aliases: Option>>, ) -> crate::Result> { @@ -175,7 +175,7 @@ impl Client { } pub async fn principal_set_capabilities( - &mut self, + &self, id: &str, capabilities: Option>>, ) -> crate::Result> { @@ -190,7 +190,7 @@ impl Client { .updated(id) } - pub async fn principal_destroy(&mut self, id: &str) -> crate::Result<()> { + pub async fn principal_destroy(&self, id: &str) -> crate::Result<()> { let mut request = self.build(); request.set_principal().destroy([id]).arguments(); request @@ -200,7 +200,7 @@ impl Client { } pub async fn principal_get( - &mut self, + &self, id: &str, properties: Option>, ) -> crate::Result> { @@ -216,7 +216,7 @@ impl Client { } pub async fn principal_query( - &mut self, + &self, filter: Option>>, sort: Option>>, ) -> crate::Result { @@ -232,7 +232,7 @@ impl Client { } pub async fn principal_changes( - &mut self, + &self, since_state: impl Into, max_changes: usize, ) -> crate::Result>> { diff --git a/src/push_subscription/helpers.rs b/src/push_subscription/helpers.rs index 53756f2..2309534 100644 --- a/src/push_subscription/helpers.rs +++ b/src/push_subscription/helpers.rs @@ -13,7 +13,7 @@ use super::{Keys, PushSubscription}; impl Client { pub async fn push_subscription_create( - &mut self, + &self, device_client_id: impl Into, url: impl Into, keys: Option, @@ -37,7 +37,7 @@ impl Client { } pub async fn push_subscription_verify( - &mut self, + &self, id: &str, verification_code: impl Into, ) -> crate::Result> { @@ -53,7 +53,7 @@ impl Client { } pub async fn push_subscription_update_types( - &mut self, + &self, id: &str, types: Option>, ) -> crate::Result> { @@ -65,7 +65,7 @@ impl Client { .updated(id) } - pub async fn push_subscription_destroy(&mut self, id: &str) -> crate::Result<()> { + pub async fn push_subscription_destroy(&self, id: &str) -> crate::Result<()> { let mut request = self.build(); request.set_push_subscription().destroy([id]); request diff --git a/src/thread/helpers.rs b/src/thread/helpers.rs index 964ae73..ddb1cff 100644 --- a/src/thread/helpers.rs +++ b/src/thread/helpers.rs @@ -12,7 +12,7 @@ use crate::{ use super::Thread; impl Client { - pub async fn thread_get(&mut self, id: &str) -> crate::Result> { + pub async fn thread_get(&self, id: &str) -> crate::Result> { let mut request = self.build(); request.get_thread().ids([id]); request diff --git a/src/vacation_response/helpers.rs b/src/vacation_response/helpers.rs index 31a7032..7bb9d52 100644 --- a/src/vacation_response/helpers.rs +++ b/src/vacation_response/helpers.rs @@ -13,7 +13,7 @@ use super::{Property, VacationResponse}; impl Client { pub async fn vacation_response_create( - &mut self, + &self, subject: impl Into, text_body: Option>, html_body: Option>, @@ -36,7 +36,7 @@ impl Client { } pub async fn vacation_response_enable( - &mut self, + &self, subject: impl Into, text_body: Option>, html_body: Option>, @@ -56,7 +56,7 @@ impl Client { .updated("singleton") } - pub async fn vacation_response_disable(&mut self) -> crate::Result> { + pub async fn vacation_response_disable(&self) -> crate::Result> { let mut request = self.build(); request .set_vacation_response() @@ -70,7 +70,7 @@ impl Client { } pub async fn vacation_response_set_dates( - &mut self, + &self, from_date: Option, to_date: Option, ) -> crate::Result> { @@ -89,7 +89,7 @@ impl Client { } pub async fn vacation_response_get( - &mut self, + &self, properties: Option>, ) -> crate::Result> { let mut request = self.build(); @@ -103,7 +103,7 @@ impl Client { .map(|mut r| r.unwrap_list().pop()) } - pub async fn vacation_response_destroy(&mut self) -> crate::Result<()> { + pub async fn vacation_response_destroy(&self) -> crate::Result<()> { let mut request = self.build(); request.set_vacation_response().destroy(["singleton"]); request