use serde::Serialize;
use std::borrow::Borrow;
mod model;
use self::model::{FindResponse, SearchResponse};
use crate::{Error, Kind, Movie, Plot, SearchResults};
async fn get_request<I, K, V>(params: I) -> Result<reqwest::Response, Error>
where
I: IntoIterator,
I::Item: Borrow<(K, V)> + Serialize,
K: AsRef<str> + Serialize,
V: AsRef<str> + Serialize,
{
const API_ENDPOINT: &str = "https://omdbapi.com";
const API_VERSION: &str = "1";
let params = params.into_iter().collect::<Vec<_>>();
let response = reqwest::Client::new()
.get(API_ENDPOINT)
.query(&[("v", API_VERSION)])
.query(&[("r", "json")])
.query(¶ms)
.send()
.await?;
let status = response.status();
if !status.is_success() {
return Err(Error::Status(status));
}
Ok(response)
}
pub fn imdb_id<S: Into<String>>(title: S) -> FindQuery {
FindQuery {
imdb_id: Some(title.into()),
..Default::default()
}
}
pub fn title<S: Into<String>>(title: S) -> FindQuery {
FindQuery {
title: Some(title.into()),
..Default::default()
}
}
pub fn search<S: Into<String>>(search: S) -> SearchQuery {
SearchQuery {
search: search.into(),
..Default::default()
}
}
#[derive(Debug)]
pub struct FindQuery {
imdb_id: Option<String>,
title: Option<String>,
apikey: Option<String>,
kind: Option<Kind>,
year: Option<String>,
plot: Option<Plot>, }
impl Default for FindQuery {
fn default() -> FindQuery {
FindQuery {
imdb_id: None,
title: None,
apikey: None,
kind: None,
year: None,
plot: None,
}
}
}
impl FindQuery {
pub fn kind(&mut self, kind: Kind) -> &mut FindQuery {
self.kind = Some(kind);
self
}
pub fn year<S: ToString>(&mut self, year: S) -> &mut FindQuery {
self.year = Some(year.to_string());
self
}
pub fn apikey<S: ToString>(&mut self, apikey: S) -> &mut FindQuery {
self.apikey = Some(apikey.to_string());
self
}
pub fn plot(&mut self, plot: Plot) -> &mut FindQuery {
self.plot = Some(plot);
self
}
pub async fn get(&self) -> Result<Movie, Error> {
let mut params: Vec<(&str, String)> = Vec::new();
if let Some(i) = self.imdb_id.as_ref() {
params.push(("i", i.clone()));
} else if let Some(t) = self.title.as_ref() {
params.push(("t", t.clone()));
}
if let Some(k) = self.apikey.as_ref() {
params.push(("apikey", k.clone()));
}
if let Some(kind) = self.kind.as_ref() {
let k: &str = (*kind).into();
params.push(("type", String::from(k)));
}
if let Some(year) = self.year.as_ref() {
params.push(("y", year.clone()));
}
if let Some(plot) = self.plot.as_ref() {
let p: &str = (*plot).into();
params.push(("plot", String::from(p)));
}
let response: FindResponse = get_request(params).await?.json().await?;
if response.response.to_lowercase() != "true" {
return Err(Error::Api(
response.error.unwrap_or_else(|| "undefined".to_owned()),
));
}
Ok(response.into())
}
}
#[derive(Debug)]
pub struct SearchQuery {
search: String,
apikey: Option<String>,
kind: Option<Kind>,
year: Option<String>,
page: Option<usize>,
}
impl Default for SearchQuery {
fn default() -> SearchQuery {
SearchQuery {
search: String::new(),
apikey: None,
kind: None,
year: None,
page: None,
}
}
}
impl SearchQuery {
pub fn apikey<S: ToString>(&mut self, apikey: S) -> &mut SearchQuery {
self.apikey = Some(apikey.to_string());
self
}
pub fn kind(&mut self, kind: Kind) -> &mut SearchQuery {
self.kind = Some(kind);
self
}
pub fn year<S: ToString>(&mut self, year: S) -> &mut SearchQuery {
self.year = Some(year.to_string());
self
}
pub fn page(&mut self, page: usize) -> &mut SearchQuery {
self.page = Some(page);
self
}
pub async fn get(&self) -> Result<SearchResults, Error> {
let mut params: Vec<(&str, String)> = Vec::new();
params.push(("s", self.search.clone()));
if let Some(k) = self.apikey.as_ref() {
params.push(("apikey", k.clone()));
}
if let Some(kind) = self.kind.as_ref() {
let k: &str = (*kind).into();
params.push(("type", String::from(k)));
}
if let Some(year) = self.year.as_ref() {
params.push(("y", year.clone()));
}
if let Some(page) = self.page.as_ref() {
params.push(("page", page.to_string()));
}
let response: SearchResponse = get_request(params).await?.json().await?;
if response.response.to_lowercase() != "true" {
return Err(Error::Api(
response.error.unwrap_or_else(|| "undefined".to_owned()),
));
}
Ok(response.into())
}
}