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