use crate::auth::auth_token;
use crate::constants::ENV_VAR_NAME;
use crate::https::{get_https_client, tls};
use crate::log::*;
use crate::models::*;
use crate::status::raise_for_status;
use crate::utils::{into_struct_from_slice, stream_reader_from_url};
use crate::RustWistiaError;
use std::borrow::Cow;
use std::io::Cursor;
use std::time::Instant;
use hyper::client::{Client, HttpConnector};
use hyper::header::AUTHORIZATION;
use hyper::{Body, Method, Request};
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use serde_json::to_vec;
use serde_urlencoded::to_string;
pub type WistiaClient<'a> = DataClient<'a>;
#[derive(Clone)]
pub struct DataClient<'a> {
pub access_token: Cow<'a, str>,
pub client: Client<tls::HttpsConnector<HttpConnector>>,
}
impl<'a> From<Cow<'a, str>> for DataClient<'a> {
fn from(access_token: Cow<'a, str>) -> Self {
let token = auth_token(&access_token);
Self {
access_token: Cow::Owned(token),
client: get_https_client(),
}
}
}
impl<'a> From<&'a str> for DataClient<'a> {
fn from(access_token: &'a str) -> Self {
Self::from(Cow::Borrowed(access_token))
}
}
impl<'a> From<String> for DataClient<'a> {
fn from(access_token: String) -> Self {
Self::from(Cow::Owned(access_token))
}
}
impl<'a> DataClient<'a> {
pub fn new(access_token: &'a str) -> Self {
Self::from(access_token)
}
pub fn from_env() -> crate::Result<Self> {
let token: String =
std::env::var(ENV_VAR_NAME).map_err(|_| RustWistiaError::EnvVarNotFound {
name: ENV_VAR_NAME.to_owned(),
})?;
Ok(Self::from(token))
}
pub async fn download_asset(
&'a self,
req: DownloadAssetRequest<'a>,
) -> crate::Result<Cursor<hyper::body::Bytes>> {
let media = if let Some(media) = req.media {
media
} else if let Some(media_id) = req.media_id {
self.get_media(media_id).await?
} else {
return Err(RustWistiaError::MediaIsRequired);
};
let url = media.asset_url(req.asset_type)?;
let media_content = stream_reader_from_url(&url, self.client.clone()).await;
if let Some(file_path) = req.file_path {
let mut content = media_content?;
let mut file = std::fs::File::create(file_path)?;
std::io::copy(&mut content, &mut file)?;
Ok(content)
} else {
media_content
}
}
pub async fn get_media(&self, video_id: &'a str) -> crate::Result<Media> {
let url = format!(
"https://api.wistia.com/v1/medias/{media_id}.json",
media_id = video_id
);
self.get(&url).await
}
pub async fn update_media(&self, video: UpdateMediaRequest) -> crate::Result<MediaInfo> {
let url = format!(
"https://api.wistia.com/v1/medias/{media_id}.json",
media_id = video.id
);
self.put(&url, video).await
}
pub async fn get<R: DeserializeOwned>(&'a self, url: &'a str) -> crate::Result<R> {
let token = self.access_token.as_ref();
let req = Request::builder()
.method(Method::GET)
.uri(url)
.header(AUTHORIZATION, token)
.body(Body::empty())?;
self.make_request(url, req).await
}
pub async fn put<B: Serialize, R: DeserializeOwned>(
&'a self,
url: &'a str,
body: B,
) -> crate::Result<R> {
let token = self.access_token.as_ref();
let mut uri: String;
let params = to_string(body)?;
let params_len = params.len();
let url = if params_len != 0 {
uri = String::with_capacity(url.len() + params.len() + 1);
uri.push_str(url);
uri.push('?');
uri.push_str(¶ms);
uri.as_str()
} else {
url
};
let req = Request::builder()
.method(Method::PUT)
.uri(url)
.header(AUTHORIZATION, token)
.body(Body::empty())?;
self.make_request(url, req).await
}
pub async fn put_with_body<B: Serialize, R: DeserializeOwned>(
&'a self,
url: &'a str,
body: B,
) -> crate::Result<R> {
let token = self.access_token.as_ref();
let body_data = to_vec(&body)?;
let req = Request::builder()
.method(Method::PUT)
.uri(url)
.header(AUTHORIZATION, token)
.body(Body::from(body_data))?;
self.make_request(url, req).await
}
pub(crate) async fn make_request<R: DeserializeOwned>(
&'a self,
url: &'a str,
req: Request<Body>,
) -> crate::Result<R> {
let start = Instant::now();
let mut resp = self.client.request(req).await?;
debug!("Call Data API completed {:.2?}", start.elapsed());
raise_for_status(url, &mut resp).await?;
into_struct_from_slice(resp).await
}
}