use reqwest;
use serde::Deserialize;
use serde_json;
use std::io::Read;
use std::path::Path;
use std::thread::sleep;
use std::time::Duration;
use url::Url;
use errors::*;
use multipart_form_data;
use resource::{self, Id, Resource, Source};
use util::StringifyError;
lazy_static! {
static ref BIGML_URL: Url = Url::parse("https://bigml.io/")
.expect("Cannot parse BigML URL in source code");
}
pub struct Client {
username: String,
api_key: String,
}
impl Client {
pub fn new<S1, S2>(username: S1, api_key: S2) -> Result<Client>
where S1: Into<String>, S2: Into<String>
{
Ok(Client {
username: username.into(),
api_key: api_key.into(),
})
}
fn auth(&self) -> String {
format!("username={};api_key={}", self.username, self.api_key)
}
fn url(&self, path: &str) -> Url {
let mut url: Url = BIGML_URL.clone();
url.set_path(path);
url.set_query(Some(&self.auth()));
url
}
pub fn create<Args>(&self, args: &Args) -> Result<Args::Resource>
where Args: resource::Args
{
let url = self.url(Args::Resource::create_path());
debug!("POST {} {:#?}", Args::Resource::create_path(), &serde_json::to_string(args));
let mkerr = || ErrorKind::CouldNotAccessUrl(url.clone());
let client = reqwest::Client::new()
.stringify_error()
.chain_err(&mkerr)?;
let res = client.post(url.clone())
.json(args)
.send()
.stringify_error()
.chain_err(&mkerr)?;
self.handle_response(res).chain_err(&mkerr)
}
pub fn create_and_wait<Args>(&self, args: &Args) -> Result<Args::Resource>
where Args: resource::Args
{
self.wait(self.create(args)?.id())
}
pub fn create_source_from_path<P>(&self, path: P) -> Result<Source>
where P: AsRef<Path>
{
let path = path.as_ref();
let mut body = multipart_form_data::Body::new("file", path)
.chain_err(|| ErrorKind::CouldNotReadFile(path.to_owned()))?;
let mut body_data = vec![];
body.read_to_end(&mut body_data)
.chain_err(|| ErrorKind::CouldNotReadFile(path.to_owned()))?;
let url = self.url("/source");
let mkerr = || ErrorKind::CouldNotAccessUrl(url.clone());
let client = reqwest::Client::new()
.stringify_error()
.chain_err(&mkerr)?;
let res = client.post(url.clone())
.header(reqwest::header::ContentType(body.mime_type()))
.body(body_data)
.send()
.stringify_error()
.chain_err(&mkerr)?;
self.handle_response(res).chain_err(&mkerr)
}
pub fn fetch<R: Resource>(&self, resource: &Id<R>) -> Result<R> {
let url = self.url(resource.as_str());
let mkerr = || ErrorKind::CouldNotAccessUrl(url.clone());
let client = reqwest::Client::new()
.stringify_error()
.chain_err(&mkerr)?;
let res = client.get(url.clone())
.send()
.stringify_error()
.chain_err(&mkerr)?;
self.handle_response(res).chain_err(&mkerr)
}
pub fn wait<R: Resource>(&self, resource: &Id<R>) -> Result<R> {
loop {
let res = self.fetch(resource)?;
if res.status().code().is_ready() {
return Ok(res);
} else if res.status().code().is_err() {
let err: Error = res.status().message().into();
let url = self.url(resource.as_str());
return Err(err)
.chain_err(|| ErrorKind::CouldNotAccessUrl(url.clone()));
}
sleep(Duration::from_secs(5));
}
}
fn handle_response<T>(&self, mut res: reqwest::Response) -> Result<T>
where T: Deserialize
{
if res.status().is_success() {
let mut body = String::new();
res.read_to_string(&mut body)?;
debug!("Success body: {}", &body);
let properties = serde_json::from_str(&body)?;
Ok(properties)
} else {
let mut body = String::new();
res.read_to_string(&mut body)?;
debug!("Error body: {}", &body);
let err: Error =
ErrorKind::UnexpectedHttpStatus(res.status().to_owned(), body).into();
Err(err)
}
}
}