1#![allow(clippy::derive_partial_eq_without_eq)]
106#![allow(clippy::too_many_arguments)]
107#![allow(clippy::nonstandard_macro_braces)]
108#![allow(clippy::large_enum_variant)]
109#![allow(clippy::tabs_in_doc_comments)]
110#![allow(missing_docs)]
111#![cfg_attr(docsrs, feature(doc_cfg))]
112
113pub mod acl;
114pub mod calendar_list;
115pub mod calendars;
116pub mod channels;
117pub mod colors;
118pub mod events;
119pub mod freebusy;
120pub mod settings;
121pub mod types;
122#[doc(hidden)]
123pub mod utils;
124
125pub use reqwest::{header::HeaderMap, StatusCode};
126
127#[derive(Debug)]
128pub struct Response<T> {
129 pub status: reqwest::StatusCode,
130 pub headers: reqwest::header::HeaderMap,
131 pub body: T,
132}
133
134impl<T> Response<T> {
135 pub fn new(status: reqwest::StatusCode, headers: reqwest::header::HeaderMap, body: T) -> Self {
136 Self {
137 status,
138 headers,
139 body,
140 }
141 }
142}
143
144type ClientResult<T> = Result<T, ClientError>;
145
146use thiserror::Error;
147
148#[derive(Debug, Error)]
150pub enum ClientError {
151 #[error("Refresh AuthToken is empty")]
154 EmptyRefreshToken,
155 #[error(transparent)]
157 FromUtf8Error(#[from] std::string::FromUtf8Error),
158 #[error(transparent)]
160 UrlParserError(#[from] url::ParseError),
161 #[error(transparent)]
163 SerdeJsonError(#[from] serde_json::Error),
164 #[error(transparent)]
166 ReqwestError(#[from] reqwest::Error),
167 #[error(transparent)]
169 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
170 #[cfg(feature = "middleware")]
171 #[error(transparent)]
173 ReqwestMiddleWareError(#[from] reqwest_middleware::Error),
174 #[error("HTTP Error. Code: {status}, message: {error}")]
176 HttpError {
177 status: http::StatusCode,
178 headers: reqwest::header::HeaderMap,
179 error: String,
180 },
181}
182
183pub const FALLBACK_HOST: &str = "https://www.googleapis.com/calendar/v3";
184
185mod progenitor_support {
186 use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
187
188 const PATH_SET: &AsciiSet = &CONTROLS
189 .add(b' ')
190 .add(b'"')
191 .add(b'#')
192 .add(b'<')
193 .add(b'>')
194 .add(b'?')
195 .add(b'`')
196 .add(b'{')
197 .add(b'}');
198
199 #[allow(dead_code)]
200 pub(crate) fn encode_path(pc: &str) -> String {
201 utf8_percent_encode(pc, PATH_SET).to_string()
202 }
203}
204
205#[derive(Debug, Default)]
206pub(crate) struct Message {
207 pub body: Option<reqwest::Body>,
208 pub content_type: Option<String>,
209}
210
211use std::convert::TryInto;
212use std::env;
213use std::ops::Add;
214use std::sync::Arc;
215use std::time::{Duration, Instant};
216use tokio::sync::RwLock;
217
218const TOKEN_ENDPOINT: &str = "https://oauth2.googleapis.com/token";
219const USER_CONSENT_ENDPOINT: &str = "https://accounts.google.com/o/oauth2/v2/auth";
220
221#[derive(Debug, Default, Clone)]
222pub struct RootDefaultServer {}
223
224impl RootDefaultServer {
225 pub fn default_url(&self) -> &str {
226 "https://www.googleapis.com/calendar/v3"
227 }
228}
229
230#[derive(Clone)]
232pub struct Client {
233 host: String,
234 host_override: Option<String>,
235 token: Arc<RwLock<InnerToken>>,
236 client_id: String,
237 client_secret: String,
238 redirect_uri: String,
239
240 auto_refresh: bool,
241 #[cfg(feature = "middleware")]
242 client: reqwest_middleware::ClientWithMiddleware,
243 #[cfg(not(feature = "middleware"))]
244 client: reqwest::Client,
245}
246
247use schemars::JsonSchema;
248use serde::{Deserialize, Serialize};
249
250#[derive(Debug, JsonSchema, Clone, Default, Serialize, Deserialize)]
251pub struct AccessToken {
252 #[serde(
253 default,
254 skip_serializing_if = "String::is_empty",
255 deserialize_with = "crate::utils::deserialize_null_string::deserialize"
256 )]
257 pub token_type: String,
258
259 #[serde(
260 default,
261 skip_serializing_if = "String::is_empty",
262 deserialize_with = "crate::utils::deserialize_null_string::deserialize"
263 )]
264 pub access_token: String,
265 #[serde(default)]
266 pub expires_in: i64,
267
268 #[serde(
269 default,
270 skip_serializing_if = "String::is_empty",
271 deserialize_with = "crate::utils::deserialize_null_string::deserialize"
272 )]
273 pub refresh_token: String,
274 #[serde(default, alias = "x_refresh_token_expires_in")]
275 pub refresh_token_expires_in: i64,
276
277 #[serde(
278 default,
279 skip_serializing_if = "String::is_empty",
280 deserialize_with = "crate::utils::deserialize_null_string::deserialize"
281 )]
282 pub scope: String,
283}
284
285const REFRESH_THRESHOLD: Duration = Duration::from_secs(60);
289
290#[derive(Debug, Clone)]
291struct InnerToken {
292 access_token: String,
293 refresh_token: String,
294 expires_at: Option<Instant>,
295}
296
297impl Client {
298 pub fn new<I, K, R, T, Q>(
304 client_id: I,
305 client_secret: K,
306 redirect_uri: R,
307 token: T,
308 refresh_token: Q,
309 ) -> Self
310 where
311 I: ToString,
312 K: ToString,
313 R: ToString,
314 T: ToString,
315 Q: ToString,
316 {
317 let retry_policy =
319 reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
320 let client = reqwest::Client::builder()
321 .redirect(reqwest::redirect::Policy::none())
322 .build();
323 match client {
324 Ok(c) => {
325 #[cfg(feature = "middleware")]
326 let client = {
327 reqwest_middleware::ClientBuilder::new(c)
328 .with(reqwest_tracing::TracingMiddleware::default())
330 .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
332 reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
333 |req: &reqwest::Request| req.try_clone().is_some(),
334 ))
335 .build()
336 };
337 #[cfg(not(feature = "middleware"))]
338 let client = c;
339
340 let host = RootDefaultServer::default().default_url().to_string();
341
342 Client {
343 host,
344 host_override: None,
345 client_id: client_id.to_string(),
346 client_secret: client_secret.to_string(),
347 redirect_uri: redirect_uri.to_string(),
348 token: Arc::new(RwLock::new(InnerToken {
349 access_token: token.to_string(),
350 refresh_token: refresh_token.to_string(),
351 expires_at: None,
352 })),
353
354 auto_refresh: false,
355 client,
356 }
357 }
358 Err(e) => panic!("creating reqwest client failed: {:?}", e),
359 }
360 }
361
362 pub fn set_auto_access_token_refresh(&mut self, enabled: bool) -> &mut Self {
364 self.auto_refresh = enabled;
365 self
366 }
367
368 pub async fn set_expires_at(&self, expires_at: Option<Instant>) -> &Self {
374 self.token.write().await.expires_at = expires_at;
375 self
376 }
377
378 pub async fn expires_at(&self) -> Option<Instant> {
381 self.token.read().await.expires_at
382 }
383
384 pub async fn set_expires_in(&self, expires_in: i64) -> &Self {
387 self.token.write().await.expires_at = Self::compute_expires_at(expires_in);
388 self
389 }
390
391 pub async fn expires_in(&self) -> Option<Duration> {
394 self.token
395 .read()
396 .await
397 .expires_at
398 .map(|i| i.duration_since(Instant::now()))
399 }
400
401 pub async fn is_expired(&self) -> Option<bool> {
404 self.token
405 .read()
406 .await
407 .expires_at
408 .map(|expiration| expiration <= Instant::now())
409 }
410
411 fn compute_expires_at(expires_in: i64) -> Option<Instant> {
412 let seconds_valid = expires_in
413 .try_into()
414 .ok()
415 .map(Duration::from_secs)
416 .and_then(|dur| dur.checked_sub(REFRESH_THRESHOLD))
417 .or_else(|| Some(Duration::from_secs(0)));
418
419 seconds_valid.map(|seconds_valid| Instant::now().add(seconds_valid))
420 }
421
422 pub fn with_host_override<H>(&mut self, host: H) -> &mut Self
424 where
425 H: ToString,
426 {
427 self.host_override = Some(host.to_string());
428 self
429 }
430
431 pub fn remove_host_override(&mut self) -> &mut Self {
433 self.host_override = None;
434 self
435 }
436
437 pub fn get_host_override(&self) -> Option<&str> {
438 self.host_override.as_deref()
439 }
440
441 pub(crate) fn url(&self, path: &str, host: Option<&str>) -> String {
442 format!(
443 "{}{}",
444 self.get_host_override()
445 .or(host)
446 .unwrap_or(self.host.as_str()),
447 path
448 )
449 }
450
451 pub async fn new_from_env<T, R>(token: T, refresh_token: R) -> Self
462 where
463 T: ToString,
464 R: ToString,
465 {
466 use base64::{engine::general_purpose::STANDARD, Engine};
467
468 let google_key = env::var("GOOGLE_KEY_ENCODED").unwrap_or_default();
469 let decoded_google_key = STANDARD.decode(google_key).unwrap();
470 let secret = yup_oauth2::parse_application_secret(decoded_google_key)
471 .expect("failed to read from google credential env var");
472
473 let client = reqwest::Client::builder()
474 .redirect(reqwest::redirect::Policy::none())
475 .build();
476 let retry_policy =
477 reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
478
479 match client {
480 Ok(c) => {
481 #[cfg(feature = "middleware")]
482 let client = {
483 reqwest_middleware::ClientBuilder::new(c)
484 .with(reqwest_tracing::TracingMiddleware::default())
486 .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
488 reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
489 |req: &reqwest::Request| req.try_clone().is_some(),
490 ))
491 .build()
492 };
493 #[cfg(not(feature = "middleware"))]
494 let client = c;
495 let host = RootDefaultServer::default().default_url().to_string();
496
497 Client {
498 host,
499 host_override: None,
500 client_id: secret.client_id.to_string(),
501 client_secret: secret.client_secret.to_string(),
502 redirect_uri: secret.redirect_uris[0].to_string(),
503 token: Arc::new(RwLock::new(InnerToken {
504 access_token: token.to_string(),
505 refresh_token: refresh_token.to_string(),
506 expires_at: None,
507 })),
508 auto_refresh: false,
509 client,
510 }
511 }
512 Err(e) => panic!("creating reqwest client failed: {:?}", e),
513 }
514 }
515
516 pub fn user_consent_url(&self, scopes: &[String]) -> String {
519 let state = uuid::Uuid::new_v4();
520
521 let url = format!(
522 "{}?client_id={}&access_type=offline&response_type=code&redirect_uri={}&state={}",
523 USER_CONSENT_ENDPOINT, self.client_id, self.redirect_uri, state
524 );
525
526 if scopes.is_empty() {
527 return url;
528 }
529
530 format!("{}&scope={}", url, scopes.join(" "))
532 }
533
534 pub async fn refresh_access_token(&self) -> ClientResult<AccessToken> {
537 let response = {
538 let refresh_token = &self.token.read().await.refresh_token;
539
540 if refresh_token.is_empty() {
541 return Err(ClientError::EmptyRefreshToken);
542 }
543
544 let mut headers = reqwest::header::HeaderMap::new();
545 headers.append(
546 reqwest::header::ACCEPT,
547 reqwest::header::HeaderValue::from_static("application/json"),
548 );
549
550 let params = [
551 ("grant_type", "refresh_token"),
552 ("refresh_token", refresh_token),
553 ("client_id", &self.client_id),
554 ("client_secret", &self.client_secret),
555 ("redirect_uri", &self.redirect_uri),
556 ];
557 let client = reqwest::Client::new();
558 client
559 .post(TOKEN_ENDPOINT)
560 .headers(headers)
561 .form(¶ms)
562 .basic_auth(&self.client_id, Some(&self.client_secret))
563 .send()
564 .await?
565 };
566
567 let t: AccessToken = response.json().await?;
569
570 let refresh_token = self.token.read().await.refresh_token.clone();
571
572 *self.token.write().await = InnerToken {
573 access_token: t.access_token.clone(),
574 refresh_token,
575 expires_at: Self::compute_expires_at(t.expires_in),
576 };
577
578 Ok(t)
579 }
580
581 pub async fn get_access_token(&mut self, code: &str, state: &str) -> ClientResult<AccessToken> {
584 let mut headers = reqwest::header::HeaderMap::new();
585 headers.append(
586 reqwest::header::ACCEPT,
587 reqwest::header::HeaderValue::from_static("application/json"),
588 );
589
590 let params = [
591 ("grant_type", "authorization_code"),
592 ("code", code),
593 ("client_id", &self.client_id),
594 ("client_secret", &self.client_secret),
595 ("redirect_uri", &self.redirect_uri),
596 ("state", state),
597 ];
598 let client = reqwest::Client::new();
599 let resp = client
600 .post(TOKEN_ENDPOINT)
601 .headers(headers)
602 .form(¶ms)
603 .basic_auth(&self.client_id, Some(&self.client_secret))
604 .send()
605 .await?;
606
607 let t: AccessToken = resp.json().await?;
609
610 *self.token.write().await = InnerToken {
611 access_token: t.access_token.clone(),
612 refresh_token: t.refresh_token.clone(),
613 expires_at: Self::compute_expires_at(t.expires_in),
614 };
615
616 Ok(t)
617 }
618
619 async fn url_and_auth(&self, uri: &str) -> ClientResult<(reqwest::Url, Option<String>)> {
620 let parsed_url = uri.parse::<reqwest::Url>()?;
621
622 let auth = format!("Bearer {}", self.token.read().await.access_token);
623 Ok((parsed_url, Some(auth)))
624 }
625
626 async fn make_request(
627 &self,
628 method: &reqwest::Method,
629 uri: &str,
630 message: Message,
631 ) -> ClientResult<reqwest::Request> {
632 let (url, auth) = self.url_and_auth(uri).await?;
633
634 let instance = <&Client>::clone(&self);
635
636 let mut req = instance.client.request(method.clone(), url);
637
638 req = req.header(
640 reqwest::header::ACCEPT,
641 reqwest::header::HeaderValue::from_static("application/json"),
642 );
643
644 if let Some(content_type) = &message.content_type {
645 req = req.header(
646 reqwest::header::CONTENT_TYPE,
647 reqwest::header::HeaderValue::from_str(content_type).unwrap(),
648 );
649 } else {
650 req = req.header(
651 reqwest::header::CONTENT_TYPE,
652 reqwest::header::HeaderValue::from_static("application/json"),
653 );
654 }
655
656 if let Some(auth_str) = auth {
657 req = req.header(http::header::AUTHORIZATION, &*auth_str);
658 }
659
660 if let Some(body) = message.body {
661 req = req.body(body);
662 }
663
664 Ok(req.build()?)
665 }
666
667 async fn request_raw(
668 &self,
669 method: reqwest::Method,
670 uri: &str,
671 message: Message,
672 ) -> ClientResult<reqwest::Response> {
673 if self.auto_refresh {
674 let expired = self.is_expired().await;
675
676 match expired {
677 Some(true) => {
680 self.refresh_access_token().await?;
681 }
682
683 Some(false) => (),
688
689 None => (),
697 }
698 }
699
700 let req = self.make_request(&method, uri, message).await?;
701 let resp = self.client.execute(req).await?;
702
703 Ok(resp)
704 }
705
706 async fn request<Out>(
707 &self,
708 method: reqwest::Method,
709 uri: &str,
710 message: Message,
711 ) -> ClientResult<crate::Response<Out>>
712 where
713 Out: serde::de::DeserializeOwned + 'static + Send,
714 {
715 let response = self.request_raw(method, uri, message).await?;
716
717 let status = response.status();
718 let headers = response.headers().clone();
719
720 let response_body = response.bytes().await?;
721
722 if status.is_success() {
723 log::debug!("Received successful response. Read payload.");
724 let parsed_response = if status == http::StatusCode::NO_CONTENT
725 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
726 {
727 serde_json::from_str("null")?
728 } else {
729 serde_json::from_slice::<Out>(&response_body)?
730 };
731 Ok(crate::Response::new(status, headers, parsed_response))
732 } else {
733 let error = if response_body.is_empty() {
734 ClientError::HttpError {
735 status,
736 headers,
737 error: "empty response".into(),
738 }
739 } else {
740 ClientError::HttpError {
741 status,
742 headers,
743 error: String::from_utf8_lossy(&response_body).into(),
744 }
745 };
746
747 Err(error)
748 }
749 }
750
751 async fn request_with_links<Out>(
752 &self,
753 method: http::Method,
754 uri: &str,
755 message: Message,
756 ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Out>)>
757 where
758 Out: serde::de::DeserializeOwned + 'static + Send,
759 {
760 let response = self.request_raw(method, uri, message).await?;
761
762 let status = response.status();
763 let headers = response.headers().clone();
764 let link = response
765 .headers()
766 .get(http::header::LINK)
767 .and_then(|l| l.to_str().ok())
768 .and_then(|l| parse_link_header::parse(l).ok())
769 .as_ref()
770 .and_then(crate::utils::next_link);
771
772 let response_body = response.bytes().await?;
773
774 if status.is_success() {
775 log::debug!("Received successful response. Read payload.");
776
777 let parsed_response = if status == http::StatusCode::NO_CONTENT
778 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
779 {
780 serde_json::from_str("null")?
781 } else {
782 serde_json::from_slice::<Out>(&response_body)?
783 };
784 Ok((link, crate::Response::new(status, headers, parsed_response)))
785 } else {
786 let error = if response_body.is_empty() {
787 ClientError::HttpError {
788 status,
789 headers,
790 error: "empty response".into(),
791 }
792 } else {
793 ClientError::HttpError {
794 status,
795 headers,
796 error: String::from_utf8_lossy(&response_body).into(),
797 }
798 };
799 Err(error)
800 }
801 }
802
803 #[allow(dead_code)]
805 async fn post_form<Out>(
806 &self,
807 uri: &str,
808 form: reqwest::multipart::Form,
809 ) -> ClientResult<crate::Response<Out>>
810 where
811 Out: serde::de::DeserializeOwned + 'static + Send,
812 {
813 let (url, auth) = self.url_and_auth(uri).await?;
814
815 let instance = <&Client>::clone(&self);
816
817 let mut req = instance.client.request(http::Method::POST, url);
818
819 req = req.header(
821 reqwest::header::ACCEPT,
822 reqwest::header::HeaderValue::from_static("application/json"),
823 );
824
825 if let Some(auth_str) = auth {
826 req = req.header(http::header::AUTHORIZATION, &*auth_str);
827 }
828
829 req = req.multipart(form);
830
831 let response = req.send().await?;
832
833 let status = response.status();
834 let headers = response.headers().clone();
835
836 let response_body = response.bytes().await?;
837
838 if status.is_success() {
839 log::debug!("Received successful response. Read payload.");
840 let parsed_response = if status == http::StatusCode::NO_CONTENT
841 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
842 {
843 serde_json::from_str("null")?
844 } else if std::any::TypeId::of::<Out>() == std::any::TypeId::of::<String>() {
845 let s = String::from_utf8(response_body.to_vec())?;
847 serde_json::from_value(serde_json::json!(&s))?
848 } else {
849 serde_json::from_slice::<Out>(&response_body)?
850 };
851 Ok(crate::Response::new(status, headers, parsed_response))
852 } else {
853 let error = if response_body.is_empty() {
854 ClientError::HttpError {
855 status,
856 headers,
857 error: "empty response".into(),
858 }
859 } else {
860 ClientError::HttpError {
861 status,
862 headers,
863 error: String::from_utf8_lossy(&response_body).into(),
864 }
865 };
866
867 Err(error)
868 }
869 }
870
871 #[allow(dead_code)]
873 async fn request_with_accept_mime<Out>(
874 &self,
875 method: reqwest::Method,
876 uri: &str,
877 accept_mime_type: &str,
878 ) -> ClientResult<crate::Response<Out>>
879 where
880 Out: serde::de::DeserializeOwned + 'static + Send,
881 {
882 let (url, auth) = self.url_and_auth(uri).await?;
883
884 let instance = <&Client>::clone(&self);
885
886 let mut req = instance.client.request(method, url);
887
888 req = req.header(
890 reqwest::header::ACCEPT,
891 reqwest::header::HeaderValue::from_str(accept_mime_type)?,
892 );
893
894 if let Some(auth_str) = auth {
895 req = req.header(http::header::AUTHORIZATION, &*auth_str);
896 }
897
898 let response = req.send().await?;
899
900 let status = response.status();
901 let headers = response.headers().clone();
902
903 let response_body = response.bytes().await?;
904
905 if status.is_success() {
906 log::debug!("Received successful response. Read payload.");
907 let parsed_response = if status == http::StatusCode::NO_CONTENT
908 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
909 {
910 serde_json::from_str("null")?
911 } else if std::any::TypeId::of::<Out>() == std::any::TypeId::of::<String>() {
912 let s = String::from_utf8(response_body.to_vec())?;
914 serde_json::from_value(serde_json::json!(&s))?
915 } else {
916 serde_json::from_slice::<Out>(&response_body)?
917 };
918 Ok(crate::Response::new(status, headers, parsed_response))
919 } else {
920 let error = if response_body.is_empty() {
921 ClientError::HttpError {
922 status,
923 headers,
924 error: "empty response".into(),
925 }
926 } else {
927 ClientError::HttpError {
928 status,
929 headers,
930 error: String::from_utf8_lossy(&response_body).into(),
931 }
932 };
933
934 Err(error)
935 }
936 }
937
938 #[allow(dead_code)]
940 async fn request_with_mime<Out>(
941 &self,
942 method: reqwest::Method,
943 uri: &str,
944 content: &[u8],
945 mime_type: &str,
946 ) -> ClientResult<crate::Response<Out>>
947 where
948 Out: serde::de::DeserializeOwned + 'static + Send,
949 {
950 let (url, auth) = self.url_and_auth(uri).await?;
951
952 let instance = <&Client>::clone(&self);
953
954 let mut req = instance.client.request(method, url);
955
956 req = req.header(
958 reqwest::header::ACCEPT,
959 reqwest::header::HeaderValue::from_static("application/json"),
960 );
961 req = req.header(
962 reqwest::header::CONTENT_TYPE,
963 reqwest::header::HeaderValue::from_bytes(mime_type.as_bytes()).unwrap(),
964 );
965 req = req.header(
967 reqwest::header::HeaderName::from_static("x-upload-content-type"),
968 reqwest::header::HeaderValue::from_static("application/octet-stream"),
969 );
970 req = req.header(
971 reqwest::header::HeaderName::from_static("x-upload-content-length"),
972 reqwest::header::HeaderValue::from_bytes(format!("{}", content.len()).as_bytes())
973 .unwrap(),
974 );
975
976 if let Some(auth_str) = auth {
977 req = req.header(http::header::AUTHORIZATION, &*auth_str);
978 }
979
980 if content.len() > 1 {
981 let b = bytes::Bytes::copy_from_slice(content);
982 req = req.body(b);
984 }
985
986 let response = req.send().await?;
987
988 let status = response.status();
989 let headers = response.headers().clone();
990
991 let response_body = response.bytes().await?;
992
993 if status.is_success() {
994 log::debug!("Received successful response. Read payload.");
995 let parsed_response = if status == http::StatusCode::NO_CONTENT
996 || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
997 {
998 serde_json::from_str("null")?
999 } else {
1000 serde_json::from_slice::<Out>(&response_body)?
1001 };
1002 Ok(crate::Response::new(status, headers, parsed_response))
1003 } else {
1004 let error = if response_body.is_empty() {
1005 ClientError::HttpError {
1006 status,
1007 headers,
1008 error: "empty response".into(),
1009 }
1010 } else {
1011 ClientError::HttpError {
1012 status,
1013 headers,
1014 error: String::from_utf8_lossy(&response_body).into(),
1015 }
1016 };
1017
1018 Err(error)
1019 }
1020 }
1021
1022 async fn request_entity<D>(
1023 &self,
1024 method: http::Method,
1025 uri: &str,
1026 message: Message,
1027 ) -> ClientResult<crate::Response<D>>
1028 where
1029 D: serde::de::DeserializeOwned + 'static + Send,
1030 {
1031 let r = self.request(method, uri, message).await?;
1032 Ok(r)
1033 }
1034
1035 #[allow(dead_code)]
1036 async fn get<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1037 where
1038 D: serde::de::DeserializeOwned + 'static + Send,
1039 {
1040 self.request_entity(http::Method::GET, uri, message).await
1041 }
1042
1043 #[allow(dead_code)]
1044 async fn get_all_pages<D>(&self, uri: &str, _message: Message) -> ClientResult<Response<Vec<D>>>
1045 where
1046 D: serde::de::DeserializeOwned + 'static + Send,
1047 {
1048 self.unfold(uri).await
1050 }
1051
1052 #[allow(dead_code)]
1054 async fn unfold<D>(&self, uri: &str) -> ClientResult<crate::Response<Vec<D>>>
1055 where
1056 D: serde::de::DeserializeOwned + 'static + Send,
1057 {
1058 let mut global_items = Vec::new();
1059 let (new_link, mut response) = self.get_pages(uri).await?;
1060 let mut link = new_link;
1061 while !response.body.is_empty() {
1062 global_items.append(&mut response.body);
1063 if let Some(url) = &link {
1065 let url = reqwest::Url::parse(&url.0)?;
1066 let (new_link, new_response) = self.get_pages_url(&url).await?;
1067 link = new_link;
1068 response = new_response;
1069 }
1070 }
1071
1072 Ok(Response::new(
1073 response.status,
1074 response.headers,
1075 global_items,
1076 ))
1077 }
1078
1079 #[allow(dead_code)]
1080 async fn get_pages<D>(
1081 &self,
1082 uri: &str,
1083 ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Vec<D>>)>
1084 where
1085 D: serde::de::DeserializeOwned + 'static + Send,
1086 {
1087 self.request_with_links(http::Method::GET, uri, Message::default())
1088 .await
1089 }
1090
1091 #[allow(dead_code)]
1092 async fn get_pages_url<D>(
1093 &self,
1094 url: &reqwest::Url,
1095 ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Vec<D>>)>
1096 where
1097 D: serde::de::DeserializeOwned + 'static + Send,
1098 {
1099 self.request_with_links(http::Method::GET, url.as_str(), Message::default())
1100 .await
1101 }
1102
1103 #[allow(dead_code)]
1104 async fn post<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1105 where
1106 D: serde::de::DeserializeOwned + 'static + Send,
1107 {
1108 self.request_entity(http::Method::POST, uri, message).await
1109 }
1110
1111 #[allow(dead_code)]
1112 async fn patch<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1113 where
1114 D: serde::de::DeserializeOwned + 'static + Send,
1115 {
1116 self.request_entity(http::Method::PATCH, uri, message).await
1117 }
1118
1119 #[allow(dead_code)]
1120 async fn put<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1121 where
1122 D: serde::de::DeserializeOwned + 'static + Send,
1123 {
1124 self.request_entity(http::Method::PUT, uri, message).await
1125 }
1126
1127 #[allow(dead_code)]
1128 async fn delete<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1129 where
1130 D: serde::de::DeserializeOwned + 'static + Send,
1131 {
1132 self.request_entity(http::Method::DELETE, uri, message)
1133 .await
1134 }
1135
1136 pub fn acl(&self) -> acl::Acl {
1138 acl::Acl::new(self.clone())
1139 }
1140
1141 pub fn calendar_list(&self) -> calendar_list::CalendarList {
1143 calendar_list::CalendarList::new(self.clone())
1144 }
1145
1146 pub fn calendars(&self) -> calendars::Calendars {
1148 calendars::Calendars::new(self.clone())
1149 }
1150
1151 pub fn channels(&self) -> channels::Channels {
1153 channels::Channels::new(self.clone())
1154 }
1155
1156 pub fn colors(&self) -> colors::Colors {
1158 colors::Colors::new(self.clone())
1159 }
1160
1161 pub fn events(&self) -> events::Events {
1163 events::Events::new(self.clone())
1164 }
1165
1166 pub fn freebusy(&self) -> freebusy::Freebusy {
1168 freebusy::Freebusy::new(self.clone())
1169 }
1170
1171 pub fn settings(&self) -> settings::Settings {
1173 settings::Settings::new(self.clone())
1174 }
1175}