use crate::constants::{ENV_VAR_NAME, UPLOAD_API};
use crate::https::{get_https_client, tls};
use crate::log::debug;
use crate::models::*;
use crate::status::raise_for_status;
use crate::types::Result;
use crate::utils::into_struct_from_slice;
use crate::RustWistiaError;
use std::env::var;
use std::time::Instant;
use hyper::body::HttpBody;
use hyper::client::HttpConnector;
use hyper::{Body, Client, Request};
use serde_urlencoded::to_string;
#[derive(Clone)]
pub struct UploadClient<B = Body> {
pub access_token: String,
client: Client<tls::HttpsConnector<HttpConnector>, B>,
}
impl<B: HttpBody + Send + 'static> From<String> for UploadClient<B>
where
<B as HttpBody>::Data: Send,
<B as HttpBody>::Error: Into<Box<(dyn std::error::Error + Send + Sync + 'static)>>,
{
fn from(token: String) -> Self {
Self {
access_token: token,
client: get_https_client(),
}
}
}
impl<B: HttpBody + Send + 'static> From<&str> for UploadClient<B>
where
<B as HttpBody>::Data: Send,
<B as HttpBody>::Error: Into<Box<(dyn std::error::Error + Send + Sync + 'static)>>,
{
fn from(token: &str) -> Self {
Self {
access_token: token.to_string(),
client: get_https_client(),
}
}
}
impl<B: HttpBody + Send + 'static> UploadClient<B>
where
<B as HttpBody>::Data: Send,
<B as HttpBody>::Error: Into<Box<(dyn std::error::Error + Send + Sync + 'static)>>,
{
pub fn from_env() -> Result<Self> {
let token = match var(ENV_VAR_NAME) {
Ok(val) => Ok(val),
Err(_) => Err(RustWistiaError::EnvVarNotFound {
name: ENV_VAR_NAME.to_owned(),
}),
}?;
Ok(Self::from(token))
}
pub fn from_token(token: &str) -> Self {
Self::from(token)
}
pub fn build_url(params: UploadRequest) -> Result<String> {
let query = to_string(params)?;
let mut url = String::with_capacity(UPLOAD_API.len() + 1 + query.len());
url.push_str(UPLOAD_API);
url.push('?');
url.push_str(query.as_str());
Ok(url)
}
pub async fn make_request<'a>(
&'a self,
url: &'a str,
req: Request<B>,
) -> Result<UploadResponse> {
let start = Instant::now();
let mut resp = self.client.request(req).await?;
debug!("Call Upload API completed {:.2?}", start.elapsed());
raise_for_status(url, &mut resp).await?;
into_struct_from_slice(resp).await
}
}
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
#[test]
fn test_url_encoded_with_struct() {
#[derive(Deserialize, Serialize, PartialEq, Debug)]
struct Meal<'a> {
bread: &'a str,
cheese: &'a str,
meat: &'a str,
fat: &'a str,
}
let m = Meal {
bread: "baguette",
cheese: "comté",
meat: "ham",
fat: "butter",
};
assert_eq!(
serde_urlencoded::to_string::<Meal>(m),
Ok("bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter".to_owned())
);
}
}