rust_wistia/api/upload/
client.rs

1use crate::constants::{ENV_VAR_NAME, UPLOAD_API};
2use crate::https::{get_https_client, tls};
3use crate::log::debug;
4use crate::models::*;
5use crate::status::raise_for_status;
6use crate::types::Result;
7use crate::utils::into_struct_from_slice;
8use crate::RustWistiaError;
9
10use std::env::var;
11use std::time::Instant;
12
13use hyper::body::HttpBody;
14use hyper::client::HttpConnector;
15use hyper::{Body, Client, Request};
16use serde_urlencoded::to_string;
17
18/// Client used to make requests  to the Wistia **[Upload API]**.
19///
20/// # Note
21/// Prefer to use this with one of the concrete implementations, i.e.
22/// [`crate::FileUploader`] or [`crate::UrlUploader`].
23///
24/// Also check out the [`rust-wistia`] docs for usage and examples.
25///
26/// [`rust-wistia`]: https://docs.rs/rust-wistia
27/// [Upload API]: https://wistia.com/support/developers/upload-api
28///
29#[derive(Clone)]
30pub struct UploadClient<B = Body> {
31    /// Represents the [API access token] used to authenticate requests to the
32    /// [Wistia API].
33    ///
34    /// [API access token]: https://wistia.com/support/developers/data-api#getting-started
35    /// [Wistia API]: https://wistia.com/support/developers/upload-api
36    pub access_token: String,
37    /// The HTTPS client to use for sending requests.
38    client: Client<tls::HttpsConnector<HttpConnector>, B>,
39}
40
41impl<B: HttpBody + Send + 'static> From<String> for UploadClient<B>
42where
43    <B as HttpBody>::Data: Send,
44    <B as HttpBody>::Error: Into<Box<(dyn std::error::Error + Send + Sync + 'static)>>,
45{
46    /// Create a new `UploadClient` from an access token
47    fn from(token: String) -> Self {
48        Self {
49            access_token: token,
50            client: get_https_client(),
51        }
52    }
53}
54
55impl<B: HttpBody + Send + 'static> From<&str> for UploadClient<B>
56where
57    <B as HttpBody>::Data: Send,
58    <B as HttpBody>::Error: Into<Box<(dyn std::error::Error + Send + Sync + 'static)>>,
59{
60    /// Create a new `UploadClient` from an access token
61    fn from(token: &str) -> Self {
62        Self {
63            access_token: token.to_string(),
64            client: get_https_client(),
65        }
66    }
67}
68
69impl<B: HttpBody + Send + 'static> UploadClient<B>
70where
71    <B as HttpBody>::Data: Send,
72    <B as HttpBody>::Error: Into<Box<(dyn std::error::Error + Send + Sync + 'static)>>,
73{
74    /// Initialize a new `UploadClient` object from an [API access token],
75    /// assuming this is currently set in the environment.
76    ///
77    /// [API access token]: https://wistia.com/support/developers/data-api#getting-started
78    pub fn from_env() -> Result<Self> {
79        let token = match var(ENV_VAR_NAME) {
80            Ok(val) => Ok(val),
81            Err(_) => Err(RustWistiaError::EnvVarNotFound {
82                name: ENV_VAR_NAME.to_owned(),
83            }),
84        }?;
85
86        Ok(Self::from(token))
87    }
88
89    /// Initialize a new `UploadClient` object from an [API access token].
90    ///
91    /// [API access token]: https://wistia.com/support/developers/data-api#getting-started
92    pub fn from_token(token: &str) -> Self {
93        Self::from(token)
94    }
95
96    /// Build the URL with the url-encoded *query parameters* included
97    pub fn build_url(params: UploadRequest) -> Result<String> {
98        let query = to_string(params)?;
99
100        // Build the URL with the query parameters included
101        let mut url = String::with_capacity(UPLOAD_API.len() + 1 + query.len());
102        url.push_str(UPLOAD_API);
103        url.push('?');
104        url.push_str(query.as_str());
105
106        Ok(url)
107    }
108
109    /// Send the request to the Wistia Upload API
110    pub async fn make_request<'a>(
111        &'a self,
112        url: &'a str,
113        req: Request<B>,
114    ) -> Result<UploadResponse> {
115        let start = Instant::now();
116        let mut resp = self.client.request(req).await?;
117        debug!("Call Upload API completed {:.2?}", start.elapsed());
118
119        raise_for_status(url, &mut resp).await?;
120
121        into_struct_from_slice(resp).await
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use serde::{Deserialize, Serialize};
128
129    #[test]
130    fn test_url_encoded_with_struct() {
131        #[derive(Deserialize, Serialize, PartialEq, Debug)]
132        struct Meal<'a> {
133            bread: &'a str,
134            cheese: &'a str,
135            meat: &'a str,
136            fat: &'a str,
137        }
138
139        let m = Meal {
140            bread: "baguette",
141            cheese: "comté",
142            meat: "ham",
143            fat: "butter",
144        };
145
146        assert_eq!(
147            serde_urlencoded::to_string::<Meal>(m),
148            Ok("bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter".to_owned())
149        );
150    }
151}