google_drive/
lib.rs

1//! A fully generated, opinionated API client library for Google Drive.
2//!
3//!
4//! [![docs.rs](https://docs.rs/google-drive/badge.svg)](https://docs.rs/google-drive)
5//!
6//! ## API Details
7//!
8//! Manages files in Drive including uploading, downloading, searching, detecting changes, and updating sharing permissions.
9//!
10//! [API Terms of Service](https://developers.google.com/terms/)
11//!
12//! ### Contact
13//!
14//!
15//! | name | url |
16//! |----|----|
17//! | Google | <https://google.com> |
18//!
19//! ### License
20//!
21//!
22//! | name | url |
23//! |----|----|
24//! | Creative Commons Attribution 3.0 | <http://creativecommons.org/licenses/by/3.0/> |
25//!
26//!
27//! ## Client Details
28//!
29//! This client is generated from the [Google Drive OpenAPI
30//! specs](https://www.googleapis.com/discovery/v1/apis/drive/v3/rest) based on API spec version `v3`. This way it will remain
31//! up to date as features are added. The documentation for the crate is generated
32//! along with the code to make this library easy to use.
33//!
34//!
35//! To install the library, add the following to your `Cargo.toml` file.
36//!
37//! ```toml
38//! [dependencies]
39//! google-drive = "0.10.0"
40//! ```
41//!
42//! ## Basic example
43//!
44//! Typical use will require intializing a `Client`. This requires
45//! a user agent string and set of credentials.
46//!
47//! ```rust
48//! use google_drive::Client;
49//!
50//! let google drive = Client::new(
51//!     String::from("client-id"),
52//!     String::from("client-secret"),
53//!     String::from("redirect-uri"),
54//!     String::from("token"),
55//!     String::from("refresh-token")
56//! );
57//! ```
58//!
59//! Alternatively, the library can search for most of the variables required for
60//! the client in the environment:
61//!
62//! - `GOOGLE DRIVE_CLIENT_ID`
63//! - `GOOGLE DRIVE_CLIENT_SECRET`
64//! - `GOOGLE DRIVE_REDIRECT_URI`
65//!
66//! And then you can create a client from the environment.
67//!
68//! ```rust
69//! use google_drive::Client;
70//!
71//! let google drive = Client::new_from_env(
72//!     String::from("token"),
73//!     String::from("refresh-token")
74//! );
75//! ```
76//!
77//! It is okay to pass empty values for `token` and `refresh_token`. In
78//! the initial state of the client, you will not know these values.
79//!
80//! To start off a fresh client and get a `token` and `refresh_token`, use the following.
81//!
82//! ```rust
83//! use google_drive::Client;
84//!
85//! async fn do_call() {
86//!     let mut google drive = Client::new_from_env("", "");
87//!
88//!     // Get the URL to request consent from the user.
89//!     // You can optionally pass in scopes. If none are provided, then the
90//!     // resulting URL will not have any scopes.
91//!     let user_consent_url = google drive.user_consent_url(&["some-scope".to_string()]);
92//!
93//!     // In your redirect URL capture the code sent and our state.
94//!     // Send it along to the request for the token.
95//!     let code = "thing-from-redirect-url";
96//!     let state = "state-from-redirect-url";
97//!     let mut access_token = google drive.get_access_token(code, state).await.unwrap();
98//!
99//!     // You can additionally refresh the access token with the following.
100//!     // You must have a refresh token to be able to call this function.
101//!     access_token = google drive.refresh_access_token().await.unwrap();
102//! }
103//! ```
104//!
105#![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 about;
114pub mod changes;
115pub mod channels;
116pub mod comments;
117pub mod drives;
118pub mod files;
119pub mod permissions;
120pub mod replies;
121pub mod revisions;
122pub mod teamdrives;
123pub mod traits;
124pub mod types;
125#[doc(hidden)]
126pub mod utils;
127
128pub use reqwest::{header::HeaderMap, StatusCode};
129
130#[derive(Debug)]
131pub struct Response<T> {
132    pub status: reqwest::StatusCode,
133    pub headers: reqwest::header::HeaderMap,
134    pub body: T,
135}
136
137impl<T> Response<T> {
138    pub fn new(status: reqwest::StatusCode, headers: reqwest::header::HeaderMap, body: T) -> Self {
139        Self {
140            status,
141            headers,
142            body,
143        }
144    }
145}
146
147type ClientResult<T> = Result<T, ClientError>;
148
149use thiserror::Error;
150
151/// Errors returned by the client
152#[derive(Debug, Error)]
153pub enum ClientError {
154    // Generic Token Client
155    /// Empty refresh auth token
156    #[error("Refresh AuthToken is empty")]
157    EmptyRefreshToken,
158    /// utf8 convertion error
159    #[error(transparent)]
160    FromUtf8Error(#[from] std::string::FromUtf8Error),
161
162    /// Google Drive not found
163    #[error("{name:?}: Drive not found")]
164    DriveNotFound { name: String },
165    /// str convertion error
166    #[error(transparent)]
167    ToStrError(#[from] reqwest::header::ToStrError),
168    /// URL Parsing Error
169    #[error(transparent)]
170    UrlParserError(#[from] url::ParseError),
171    /// Serde JSON parsing error
172    #[error(transparent)]
173    SerdeJsonError(#[from] serde_json::Error),
174    /// Errors returned by reqwest
175    #[error(transparent)]
176    ReqwestError(#[from] reqwest::Error),
177    /// Errors returned by reqwest::header
178    #[error(transparent)]
179    InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
180    #[cfg(feature = "middleware")]
181    /// Errors returned by reqwest middleware
182    #[error(transparent)]
183    ReqwestMiddleWareError(#[from] reqwest_middleware::Error),
184    /// Generic HTTP Error
185    #[error("HTTP Error. Code: {status}, message: {error}")]
186    HttpError {
187        status: http::StatusCode,
188        headers: reqwest::header::HeaderMap,
189        error: String,
190    },
191}
192
193pub const FALLBACK_HOST: &str = "https://www.googleapis.com/drive/v3";
194
195mod progenitor_support {
196    use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
197
198    const PATH_SET: &AsciiSet = &CONTROLS
199        .add(b' ')
200        .add(b'"')
201        .add(b'#')
202        .add(b'<')
203        .add(b'>')
204        .add(b'?')
205        .add(b'`')
206        .add(b'{')
207        .add(b'}');
208
209    #[allow(dead_code)]
210    pub(crate) fn encode_path(pc: &str) -> String {
211        utf8_percent_encode(pc, PATH_SET).to_string()
212    }
213}
214
215#[derive(Debug, Default)]
216pub(crate) struct Message {
217    pub body: Option<reqwest::Body>,
218    pub content_type: Option<String>,
219}
220
221use std::convert::TryInto;
222use std::env;
223use std::ops::Add;
224use std::sync::Arc;
225use std::time::{Duration, Instant};
226use tokio::sync::RwLock;
227
228const TOKEN_ENDPOINT: &str = "https://oauth2.googleapis.com/token";
229const USER_CONSENT_ENDPOINT: &str = "https://accounts.google.com/o/oauth2/v2/auth";
230
231#[derive(Debug, Default, Clone)]
232pub struct RootDefaultServer {}
233
234impl RootDefaultServer {
235    pub fn default_url(&self) -> &str {
236        "https://www.googleapis.com/drive/v3"
237    }
238}
239
240/// Entrypoint for interacting with the API client.
241#[derive(Clone)]
242pub struct Client {
243    host: String,
244    host_override: Option<String>,
245    token: Arc<RwLock<InnerToken>>,
246    client_id: String,
247    client_secret: String,
248    redirect_uri: String,
249
250    auto_refresh: bool,
251    #[cfg(feature = "middleware")]
252    client: reqwest_middleware::ClientWithMiddleware,
253    #[cfg(not(feature = "middleware"))]
254    client: reqwest::Client,
255}
256
257use schemars::JsonSchema;
258use serde::{Deserialize, Serialize};
259
260#[derive(Debug, JsonSchema, Clone, Default, Serialize, Deserialize)]
261pub struct AccessToken {
262    #[serde(
263        default,
264        skip_serializing_if = "String::is_empty",
265        deserialize_with = "crate::utils::deserialize_null_string::deserialize"
266    )]
267    pub token_type: String,
268
269    #[serde(
270        default,
271        skip_serializing_if = "String::is_empty",
272        deserialize_with = "crate::utils::deserialize_null_string::deserialize"
273    )]
274    pub access_token: String,
275    #[serde(default)]
276    pub expires_in: i64,
277
278    #[serde(
279        default,
280        skip_serializing_if = "String::is_empty",
281        deserialize_with = "crate::utils::deserialize_null_string::deserialize"
282    )]
283    pub refresh_token: String,
284    #[serde(default, alias = "x_refresh_token_expires_in")]
285    pub refresh_token_expires_in: i64,
286
287    #[serde(
288        default,
289        skip_serializing_if = "String::is_empty",
290        deserialize_with = "crate::utils::deserialize_null_string::deserialize"
291    )]
292    pub scope: String,
293}
294
295/// Time in seconds before the access token expiration point that a refresh should
296/// be performed. This value is subtracted from the `expires_in` value returned by
297/// the provider prior to storing
298const REFRESH_THRESHOLD: Duration = Duration::from_secs(60);
299
300#[derive(Debug, Clone)]
301struct InnerToken {
302    access_token: String,
303    refresh_token: String,
304    expires_at: Option<Instant>,
305}
306
307impl Client {
308    /// Create a new Client struct. Requires OAuth2 configuration values as well as an access and refresh token.
309    ///
310    /// # Panics
311    ///
312    /// This function will panic if the internal http client fails to create
313    pub fn new<I, K, R, T, Q>(
314        client_id: I,
315        client_secret: K,
316        redirect_uri: R,
317        token: T,
318        refresh_token: Q,
319    ) -> Self
320    where
321        I: ToString,
322        K: ToString,
323        R: ToString,
324        T: ToString,
325        Q: ToString,
326    {
327        // Retry up to 3 times with increasing intervals between attempts.
328        let retry_policy =
329            reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
330        let client = reqwest::Client::builder()
331            .redirect(reqwest::redirect::Policy::none())
332            .build();
333        match client {
334            Ok(c) => {
335                #[cfg(feature = "middleware")]
336                let client = {
337                    reqwest_middleware::ClientBuilder::new(c)
338                        // Trace HTTP requests. See the tracing crate to make use of these traces.
339                        .with(reqwest_tracing::TracingMiddleware::default())
340                        // Retry failed requests.
341                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
342                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
343                            |req: &reqwest::Request| req.try_clone().is_some(),
344                        ))
345                        .build()
346                };
347                #[cfg(not(feature = "middleware"))]
348                let client = c;
349
350                let host = RootDefaultServer::default().default_url().to_string();
351
352                Client {
353                    host,
354                    host_override: None,
355                    client_id: client_id.to_string(),
356                    client_secret: client_secret.to_string(),
357                    redirect_uri: redirect_uri.to_string(),
358                    token: Arc::new(RwLock::new(InnerToken {
359                        access_token: token.to_string(),
360                        refresh_token: refresh_token.to_string(),
361                        expires_at: None,
362                    })),
363
364                    auto_refresh: false,
365                    client,
366                }
367            }
368            Err(e) => panic!("creating reqwest client failed: {:?}", e),
369        }
370    }
371
372    /// Enables or disables the automatic refreshing of access tokens upon expiration
373    pub fn set_auto_access_token_refresh(&mut self, enabled: bool) -> &mut Self {
374        self.auto_refresh = enabled;
375        self
376    }
377
378    /// Sets a specific `Instant` at which the access token should be considered expired.
379    /// The expiration value will only be used when automatic access token refreshing is
380    /// also enabled. `None` may be passed in if the expiration is unknown. In this case
381    /// automatic refreshes will be attempted when encountering an UNAUTHENTICATED status
382    /// code on a response.
383    pub async fn set_expires_at(&self, expires_at: Option<Instant>) -> &Self {
384        self.token.write().await.expires_at = expires_at;
385        self
386    }
387
388    /// Gets the `Instant` at which the access token used by this client is set to expire
389    /// if one is known
390    pub async fn expires_at(&self) -> Option<Instant> {
391        self.token.read().await.expires_at
392    }
393
394    /// Sets the number of seconds in which the current access token should be considered
395    /// expired
396    pub async fn set_expires_in(&self, expires_in: i64) -> &Self {
397        self.token.write().await.expires_at = Self::compute_expires_at(expires_in);
398        self
399    }
400
401    /// Gets the number of seconds from now in which the current access token will be
402    /// considered expired if one is known
403    pub async fn expires_in(&self) -> Option<Duration> {
404        self.token
405            .read()
406            .await
407            .expires_at
408            .map(|i| i.duration_since(Instant::now()))
409    }
410
411    /// Determines if the access token currently stored in the client is expired. If the
412    /// expiration can not be determined, None is returned
413    pub async fn is_expired(&self) -> Option<bool> {
414        self.token
415            .read()
416            .await
417            .expires_at
418            .map(|expiration| expiration <= Instant::now())
419    }
420
421    fn compute_expires_at(expires_in: i64) -> Option<Instant> {
422        let seconds_valid = expires_in
423            .try_into()
424            .ok()
425            .map(Duration::from_secs)
426            .and_then(|dur| dur.checked_sub(REFRESH_THRESHOLD))
427            .or_else(|| Some(Duration::from_secs(0)));
428
429        seconds_valid.map(|seconds_valid| Instant::now().add(seconds_valid))
430    }
431
432    /// Override the host for all endpoins in the client.
433    pub fn with_host_override<H>(&mut self, host: H) -> &mut Self
434    where
435        H: ToString,
436    {
437        self.host_override = Some(host.to_string());
438        self
439    }
440
441    /// Disables the global host override for the client.
442    pub fn remove_host_override(&mut self) -> &mut Self {
443        self.host_override = None;
444        self
445    }
446
447    pub fn get_host_override(&self) -> Option<&str> {
448        self.host_override.as_deref()
449    }
450
451    pub(crate) fn url(&self, path: &str, host: Option<&str>) -> String {
452        format!(
453            "{}{}",
454            self.get_host_override()
455                .or(host)
456                .unwrap_or(self.host.as_str()),
457            path
458        )
459    }
460
461    /// Create a new Client struct from environment variables. Requires an existing access and refresh token
462    ///
463    /// The following environment variables are expected to be set:
464    ///   * `GOOGLE_KEY_ENCODED` - A base64 encoded version of JSON formatted application secret
465    ///
466    /// # Panics
467    ///
468    /// This function will panic if an application secret can not be parsed from the encoded key
469    ///
470    /// This function will panic if the internal http client fails to create
471    pub async fn new_from_env<T, R>(token: T, refresh_token: R) -> Self
472    where
473        T: ToString,
474        R: ToString,
475    {
476        use base64::{engine::general_purpose::STANDARD, Engine};
477
478        let google_key = env::var("GOOGLE_KEY_ENCODED").unwrap_or_default();
479        let decoded_google_key = STANDARD.decode(google_key).unwrap();
480        let secret = yup_oauth2::parse_application_secret(decoded_google_key)
481            .expect("failed to read from google credential env var");
482
483        let client = reqwest::Client::builder()
484            .redirect(reqwest::redirect::Policy::none())
485            .build();
486        let retry_policy =
487            reqwest_retry::policies::ExponentialBackoff::builder().build_with_max_retries(3);
488
489        match client {
490            Ok(c) => {
491                #[cfg(feature = "middleware")]
492                let client = {
493                    reqwest_middleware::ClientBuilder::new(c)
494                        // Trace HTTP requests. See the tracing crate to make use of these traces.
495                        .with(reqwest_tracing::TracingMiddleware::default())
496                        // Retry failed requests.
497                        .with(reqwest_conditional_middleware::ConditionalMiddleware::new(
498                            reqwest_retry::RetryTransientMiddleware::new_with_policy(retry_policy),
499                            |req: &reqwest::Request| req.try_clone().is_some(),
500                        ))
501                        .build()
502                };
503                #[cfg(not(feature = "middleware"))]
504                let client = c;
505                let host = RootDefaultServer::default().default_url().to_string();
506
507                Client {
508                    host,
509                    host_override: None,
510                    client_id: secret.client_id.to_string(),
511                    client_secret: secret.client_secret.to_string(),
512                    redirect_uri: secret.redirect_uris[0].to_string(),
513                    token: Arc::new(RwLock::new(InnerToken {
514                        access_token: token.to_string(),
515                        refresh_token: refresh_token.to_string(),
516                        expires_at: None,
517                    })),
518                    auto_refresh: false,
519                    client,
520                }
521            }
522            Err(e) => panic!("creating reqwest client failed: {:?}", e),
523        }
524    }
525
526    /// Return a user consent url with an optional set of scopes.
527    /// If no scopes are provided, they will not be passed in the url.
528    pub fn user_consent_url(&self, scopes: &[String]) -> String {
529        let state = uuid::Uuid::new_v4();
530
531        let url = format!(
532            "{}?client_id={}&access_type=offline&response_type=code&redirect_uri={}&state={}",
533            USER_CONSENT_ENDPOINT, self.client_id, self.redirect_uri, state
534        );
535
536        if scopes.is_empty() {
537            return url;
538        }
539
540        // Add the scopes.
541        format!("{}&scope={}", url, scopes.join(" "))
542    }
543
544    /// Refresh an access token from a refresh token. Client must have a refresh token
545    /// for this to work.
546    pub async fn refresh_access_token(&self) -> ClientResult<AccessToken> {
547        let response = {
548            let refresh_token = &self.token.read().await.refresh_token;
549
550            if refresh_token.is_empty() {
551                return Err(ClientError::EmptyRefreshToken);
552            }
553
554            let mut headers = reqwest::header::HeaderMap::new();
555            headers.append(
556                reqwest::header::ACCEPT,
557                reqwest::header::HeaderValue::from_static("application/json"),
558            );
559
560            let params = [
561                ("grant_type", "refresh_token"),
562                ("refresh_token", refresh_token),
563                ("client_id", &self.client_id),
564                ("client_secret", &self.client_secret),
565                ("redirect_uri", &self.redirect_uri),
566            ];
567            let client = reqwest::Client::new();
568            client
569                .post(TOKEN_ENDPOINT)
570                .headers(headers)
571                .form(&params)
572                .basic_auth(&self.client_id, Some(&self.client_secret))
573                .send()
574                .await?
575        };
576
577        // Unwrap the response.
578        let t: AccessToken = response.json().await?;
579
580        let refresh_token = self.token.read().await.refresh_token.clone();
581
582        *self.token.write().await = InnerToken {
583            access_token: t.access_token.clone(),
584            refresh_token,
585            expires_at: Self::compute_expires_at(t.expires_in),
586        };
587
588        Ok(t)
589    }
590
591    /// Get an access token from the code returned by the URL paramter sent to the
592    /// redirect URL.
593    pub async fn get_access_token(&mut self, code: &str, state: &str) -> ClientResult<AccessToken> {
594        let mut headers = reqwest::header::HeaderMap::new();
595        headers.append(
596            reqwest::header::ACCEPT,
597            reqwest::header::HeaderValue::from_static("application/json"),
598        );
599
600        let params = [
601            ("grant_type", "authorization_code"),
602            ("code", code),
603            ("client_id", &self.client_id),
604            ("client_secret", &self.client_secret),
605            ("redirect_uri", &self.redirect_uri),
606            ("state", state),
607        ];
608        let client = reqwest::Client::new();
609        let resp = client
610            .post(TOKEN_ENDPOINT)
611            .headers(headers)
612            .form(&params)
613            .basic_auth(&self.client_id, Some(&self.client_secret))
614            .send()
615            .await?;
616
617        // Unwrap the response.
618        let t: AccessToken = resp.json().await?;
619
620        *self.token.write().await = InnerToken {
621            access_token: t.access_token.clone(),
622            refresh_token: t.refresh_token.clone(),
623            expires_at: Self::compute_expires_at(t.expires_in),
624        };
625
626        Ok(t)
627    }
628
629    async fn url_and_auth(&self, uri: &str) -> ClientResult<(reqwest::Url, Option<String>)> {
630        let parsed_url = uri.parse::<reqwest::Url>()?;
631
632        let auth = format!("Bearer {}", self.token.read().await.access_token);
633        Ok((parsed_url, Some(auth)))
634    }
635
636    async fn make_request(
637        &self,
638        method: &reqwest::Method,
639        uri: &str,
640        message: Message,
641    ) -> ClientResult<reqwest::Request> {
642        let (url, auth) = self.url_and_auth(uri).await?;
643
644        let instance = <&Client>::clone(&self);
645
646        let mut req = instance.client.request(method.clone(), url);
647
648        // Set the default headers.
649        req = req.header(
650            reqwest::header::ACCEPT,
651            reqwest::header::HeaderValue::from_static("application/json"),
652        );
653
654        if let Some(content_type) = &message.content_type {
655            req = req.header(
656                reqwest::header::CONTENT_TYPE,
657                reqwest::header::HeaderValue::from_str(content_type).unwrap(),
658            );
659        } else {
660            req = req.header(
661                reqwest::header::CONTENT_TYPE,
662                reqwest::header::HeaderValue::from_static("application/json"),
663            );
664        }
665
666        if let Some(auth_str) = auth {
667            req = req.header(http::header::AUTHORIZATION, &*auth_str);
668        }
669
670        if let Some(body) = message.body {
671            req = req.body(body);
672        }
673
674        Ok(req.build()?)
675    }
676
677    async fn request_raw(
678        &self,
679        method: reqwest::Method,
680        uri: &str,
681        message: Message,
682    ) -> ClientResult<reqwest::Response> {
683        if self.auto_refresh {
684            let expired = self.is_expired().await;
685
686            match expired {
687                // We have a known expired token, we know we need to perform a refresh prior to
688                // attempting to make a request
689                Some(true) => {
690                    self.refresh_access_token().await?;
691                }
692
693                // We have a (theoretically) known good token available. We make an optimistic
694                // attempting at the request. If the token is no longer good, then something other
695                // than the expiration is triggering the failure. We defer handling of these errors
696                // to the caller
697                Some(false) => (),
698
699                // We do not know what state we are in. We could have a valid or expired token.
700                // Generally this means we are in one of two cases:
701                //   1. We have not yet performed a token refresh, nor has the user provided
702                //      expiration data, and therefore do not know the expiration of the user
703                //      provided token
704                //   2. The provider is returning unusable expiration times, at which point we
705                //      choose to ignore them
706                None => (),
707            }
708        }
709
710        let req = self.make_request(&method, uri, message).await?;
711        let resp = self.client.execute(req).await?;
712
713        Ok(resp)
714    }
715
716    async fn request<Out>(
717        &self,
718        method: reqwest::Method,
719        uri: &str,
720        message: Message,
721    ) -> ClientResult<crate::Response<Out>>
722    where
723        Out: serde::de::DeserializeOwned + 'static + Send,
724    {
725        let response = self.request_raw(method, uri, message).await?;
726
727        let status = response.status();
728        let headers = response.headers().clone();
729
730        let response_body = response.bytes().await?;
731
732        if status.is_success() {
733            log::debug!("Received successful response. Read payload.");
734            let parsed_response = if status == http::StatusCode::NO_CONTENT
735                || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
736            {
737                serde_json::from_str("null")?
738            } else {
739                serde_json::from_slice::<Out>(&response_body)?
740            };
741            Ok(crate::Response::new(status, headers, parsed_response))
742        } else {
743            let error = if response_body.is_empty() {
744                ClientError::HttpError {
745                    status,
746                    headers,
747                    error: "empty response".into(),
748                }
749            } else {
750                ClientError::HttpError {
751                    status,
752                    headers,
753                    error: String::from_utf8_lossy(&response_body).into(),
754                }
755            };
756
757            Err(error)
758        }
759    }
760
761    async fn request_with_links<Out>(
762        &self,
763        method: http::Method,
764        uri: &str,
765        message: Message,
766    ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Out>)>
767    where
768        Out: serde::de::DeserializeOwned + 'static + Send,
769    {
770        let response = self.request_raw(method, uri, message).await?;
771
772        let status = response.status();
773        let headers = response.headers().clone();
774        let link = response
775            .headers()
776            .get(http::header::LINK)
777            .and_then(|l| l.to_str().ok())
778            .and_then(|l| parse_link_header::parse(l).ok())
779            .as_ref()
780            .and_then(crate::utils::next_link);
781
782        let response_body = response.bytes().await?;
783
784        if status.is_success() {
785            log::debug!("Received successful response. Read payload.");
786
787            let parsed_response = if status == http::StatusCode::NO_CONTENT
788                || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
789            {
790                serde_json::from_str("null")?
791            } else {
792                serde_json::from_slice::<Out>(&response_body)?
793            };
794            Ok((link, crate::Response::new(status, headers, parsed_response)))
795        } else {
796            let error = if response_body.is_empty() {
797                ClientError::HttpError {
798                    status,
799                    headers,
800                    error: "empty response".into(),
801                }
802            } else {
803                ClientError::HttpError {
804                    status,
805                    headers,
806                    error: String::from_utf8_lossy(&response_body).into(),
807                }
808            };
809            Err(error)
810        }
811    }
812
813    /* TODO: make this more DRY */
814    #[allow(dead_code)]
815    async fn post_form<Out>(
816        &self,
817        uri: &str,
818        form: reqwest::multipart::Form,
819    ) -> ClientResult<crate::Response<Out>>
820    where
821        Out: serde::de::DeserializeOwned + 'static + Send,
822    {
823        let (url, auth) = self.url_and_auth(uri).await?;
824
825        let instance = <&Client>::clone(&self);
826
827        let mut req = instance.client.request(http::Method::POST, url);
828
829        // Set the default headers.
830        req = req.header(
831            reqwest::header::ACCEPT,
832            reqwest::header::HeaderValue::from_static("application/json"),
833        );
834
835        if let Some(auth_str) = auth {
836            req = req.header(http::header::AUTHORIZATION, &*auth_str);
837        }
838
839        req = req.multipart(form);
840
841        let response = req.send().await?;
842
843        let status = response.status();
844        let headers = response.headers().clone();
845
846        let response_body = response.bytes().await?;
847
848        if status.is_success() {
849            log::debug!("Received successful response. Read payload.");
850            let parsed_response = if status == http::StatusCode::NO_CONTENT
851                || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
852            {
853                serde_json::from_str("null")?
854            } else if std::any::TypeId::of::<Out>() == std::any::TypeId::of::<String>() {
855                // Parse the output as a string.
856                let s = String::from_utf8(response_body.to_vec())?;
857                serde_json::from_value(serde_json::json!(&s))?
858            } else {
859                serde_json::from_slice::<Out>(&response_body)?
860            };
861            Ok(crate::Response::new(status, headers, parsed_response))
862        } else {
863            let error = if response_body.is_empty() {
864                ClientError::HttpError {
865                    status,
866                    headers,
867                    error: "empty response".into(),
868                }
869            } else {
870                ClientError::HttpError {
871                    status,
872                    headers,
873                    error: String::from_utf8_lossy(&response_body).into(),
874                }
875            };
876
877            Err(error)
878        }
879    }
880
881    /* TODO: make this more DRY */
882    #[allow(dead_code)]
883    async fn request_with_accept_mime<Out>(
884        &self,
885        method: reqwest::Method,
886        uri: &str,
887        accept_mime_type: &str,
888    ) -> ClientResult<crate::Response<Out>>
889    where
890        Out: serde::de::DeserializeOwned + 'static + Send,
891    {
892        let (url, auth) = self.url_and_auth(uri).await?;
893
894        let instance = <&Client>::clone(&self);
895
896        let mut req = instance.client.request(method, url);
897
898        // Set the default headers.
899        req = req.header(
900            reqwest::header::ACCEPT,
901            reqwest::header::HeaderValue::from_str(accept_mime_type)?,
902        );
903
904        if let Some(auth_str) = auth {
905            req = req.header(http::header::AUTHORIZATION, &*auth_str);
906        }
907
908        let response = req.send().await?;
909
910        let status = response.status();
911        let headers = response.headers().clone();
912
913        let response_body = response.bytes().await?;
914
915        if status.is_success() {
916            log::debug!("Received successful response. Read payload.");
917            let parsed_response = if status == http::StatusCode::NO_CONTENT
918                || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
919            {
920                serde_json::from_str("null")?
921            } else if std::any::TypeId::of::<Out>() == std::any::TypeId::of::<String>() {
922                // Parse the output as a string.
923                let s = String::from_utf8(response_body.to_vec())?;
924                serde_json::from_value(serde_json::json!(&s))?
925            } else {
926                serde_json::from_slice::<Out>(&response_body)?
927            };
928            Ok(crate::Response::new(status, headers, parsed_response))
929        } else {
930            let error = if response_body.is_empty() {
931                ClientError::HttpError {
932                    status,
933                    headers,
934                    error: "empty response".into(),
935                }
936            } else {
937                ClientError::HttpError {
938                    status,
939                    headers,
940                    error: String::from_utf8_lossy(&response_body).into(),
941                }
942            };
943
944            Err(error)
945        }
946    }
947
948    /* TODO: make this more DRY */
949    #[allow(dead_code)]
950    async fn request_with_mime<Out>(
951        &self,
952        method: reqwest::Method,
953        uri: &str,
954        content: &[u8],
955        mime_type: &str,
956    ) -> ClientResult<crate::Response<Out>>
957    where
958        Out: serde::de::DeserializeOwned + 'static + Send,
959    {
960        let (url, auth) = self.url_and_auth(uri).await?;
961
962        let instance = <&Client>::clone(&self);
963
964        let mut req = instance.client.request(method, url);
965
966        // Set the default headers.
967        req = req.header(
968            reqwest::header::ACCEPT,
969            reqwest::header::HeaderValue::from_static("application/json"),
970        );
971        req = req.header(
972            reqwest::header::CONTENT_TYPE,
973            reqwest::header::HeaderValue::from_bytes(mime_type.as_bytes()).unwrap(),
974        );
975        // We are likely uploading a file so add the right headers.
976        req = req.header(
977            reqwest::header::HeaderName::from_static("x-upload-content-type"),
978            reqwest::header::HeaderValue::from_static("application/octet-stream"),
979        );
980        req = req.header(
981            reqwest::header::HeaderName::from_static("x-upload-content-length"),
982            reqwest::header::HeaderValue::from_bytes(format!("{}", content.len()).as_bytes())
983                .unwrap(),
984        );
985
986        if let Some(auth_str) = auth {
987            req = req.header(http::header::AUTHORIZATION, &*auth_str);
988        }
989
990        if content.len() > 1 {
991            let b = bytes::Bytes::copy_from_slice(content);
992            // We are uploading a file so add that as the body.
993            req = req.body(b);
994        }
995
996        let response = req.send().await?;
997
998        let status = response.status();
999        let headers = response.headers().clone();
1000
1001        let response_body = response.bytes().await?;
1002
1003        if status.is_success() {
1004            log::debug!("Received successful response. Read payload.");
1005            let parsed_response = if status == http::StatusCode::NO_CONTENT
1006                || std::any::TypeId::of::<Out>() == std::any::TypeId::of::<()>()
1007            {
1008                serde_json::from_str("null")?
1009            } else {
1010                serde_json::from_slice::<Out>(&response_body)?
1011            };
1012            Ok(crate::Response::new(status, headers, parsed_response))
1013        } else {
1014            let error = if response_body.is_empty() {
1015                ClientError::HttpError {
1016                    status,
1017                    headers,
1018                    error: "empty response".into(),
1019                }
1020            } else {
1021                ClientError::HttpError {
1022                    status,
1023                    headers,
1024                    error: String::from_utf8_lossy(&response_body).into(),
1025                }
1026            };
1027
1028            Err(error)
1029        }
1030    }
1031
1032    async fn request_entity<D>(
1033        &self,
1034        method: http::Method,
1035        uri: &str,
1036        message: Message,
1037    ) -> ClientResult<crate::Response<D>>
1038    where
1039        D: serde::de::DeserializeOwned + 'static + Send,
1040    {
1041        let r = self.request(method, uri, message).await?;
1042        Ok(r)
1043    }
1044
1045    #[allow(dead_code)]
1046    async fn get<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1047    where
1048        D: serde::de::DeserializeOwned + 'static + Send,
1049    {
1050        self.request_entity(http::Method::GET, uri, message).await
1051    }
1052
1053    #[allow(dead_code)]
1054    async fn get_all_pages<D>(&self, uri: &str, _message: Message) -> ClientResult<Response<Vec<D>>>
1055    where
1056        D: serde::de::DeserializeOwned + 'static + Send,
1057    {
1058        // TODO: implement this.
1059        self.unfold(uri).await
1060    }
1061
1062    /// "unfold" paginated results of a vector of items
1063    #[allow(dead_code)]
1064    async fn unfold<D>(&self, uri: &str) -> ClientResult<crate::Response<Vec<D>>>
1065    where
1066        D: serde::de::DeserializeOwned + 'static + Send,
1067    {
1068        let mut global_items = Vec::new();
1069        let (new_link, mut response) = self.get_pages(uri).await?;
1070        let mut link = new_link;
1071        while !response.body.is_empty() {
1072            global_items.append(&mut response.body);
1073            // We need to get the next link.
1074            if let Some(url) = &link {
1075                let url = reqwest::Url::parse(&url.0)?;
1076                let (new_link, new_response) = self.get_pages_url(&url).await?;
1077                link = new_link;
1078                response = new_response;
1079            }
1080        }
1081
1082        Ok(Response::new(
1083            response.status,
1084            response.headers,
1085            global_items,
1086        ))
1087    }
1088
1089    #[allow(dead_code)]
1090    async fn get_pages<D>(
1091        &self,
1092        uri: &str,
1093    ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Vec<D>>)>
1094    where
1095        D: serde::de::DeserializeOwned + 'static + Send,
1096    {
1097        self.request_with_links(http::Method::GET, uri, Message::default())
1098            .await
1099    }
1100
1101    #[allow(dead_code)]
1102    async fn get_pages_url<D>(
1103        &self,
1104        url: &reqwest::Url,
1105    ) -> ClientResult<(Option<crate::utils::NextLink>, crate::Response<Vec<D>>)>
1106    where
1107        D: serde::de::DeserializeOwned + 'static + Send,
1108    {
1109        self.request_with_links(http::Method::GET, url.as_str(), Message::default())
1110            .await
1111    }
1112
1113    #[allow(dead_code)]
1114    async fn post<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::POST, uri, message).await
1119    }
1120
1121    #[allow(dead_code)]
1122    async fn patch<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::PATCH, uri, message).await
1127    }
1128
1129    #[allow(dead_code)]
1130    async fn put<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1131    where
1132        D: serde::de::DeserializeOwned + 'static + Send,
1133    {
1134        self.request_entity(http::Method::PUT, uri, message).await
1135    }
1136
1137    #[allow(dead_code)]
1138    async fn delete<D>(&self, uri: &str, message: Message) -> ClientResult<crate::Response<D>>
1139    where
1140        D: serde::de::DeserializeOwned + 'static + Send,
1141    {
1142        self.request_entity(http::Method::DELETE, uri, message)
1143            .await
1144    }
1145
1146    /// Return a reference to an interface that provides access to about operations.
1147    pub fn about(&self) -> about::About {
1148        about::About::new(self.clone())
1149    }
1150
1151    /// Return a reference to an interface that provides access to changes operations.
1152    pub fn changes(&self) -> changes::Changes {
1153        changes::Changes::new(self.clone())
1154    }
1155
1156    /// Return a reference to an interface that provides access to channels operations.
1157    pub fn channels(&self) -> channels::Channels {
1158        channels::Channels::new(self.clone())
1159    }
1160
1161    /// Return a reference to an interface that provides access to comments operations.
1162    pub fn comments(&self) -> comments::Comments {
1163        comments::Comments::new(self.clone())
1164    }
1165
1166    /// Return a reference to an interface that provides access to drives operations.
1167    pub fn drives(&self) -> drives::Drives {
1168        drives::Drives::new(self.clone())
1169    }
1170
1171    /// Return a reference to an interface that provides access to files operations.
1172    pub fn files(&self) -> files::Files {
1173        files::Files::new(self.clone())
1174    }
1175
1176    /// Return a reference to an interface that provides access to permissions operations.
1177    pub fn permissions(&self) -> permissions::Permissions {
1178        permissions::Permissions::new(self.clone())
1179    }
1180
1181    /// Return a reference to an interface that provides access to replies operations.
1182    pub fn replies(&self) -> replies::Replies {
1183        replies::Replies::new(self.clone())
1184    }
1185
1186    /// Return a reference to an interface that provides access to revisions operations.
1187    pub fn revisions(&self) -> revisions::Revisions {
1188        revisions::Revisions::new(self.clone())
1189    }
1190
1191    /// Return a reference to an interface that provides access to teamdrives operations.
1192    pub fn teamdrives(&self) -> teamdrives::Teamdrives {
1193        teamdrives::Teamdrives::new(self.clone())
1194    }
1195}