use error_chain::error_chain;
use reqwest::blocking::Response;
use reqwest::StatusCode;
use serde::de::DeserializeOwned;
use std::collections::HashMap;
use std::io::Read;
const BASE_URL: &str = "https://api.datamaxiplus.com/api/v1";
pub trait Datamaxi {
fn new(api_key: String) -> Self;
fn new_with_base_url(api_key: String, base_url: String) -> Self;
}
pub struct Config {
pub base_url: Option<String>,
pub api_key: String,
}
#[derive(Clone)]
pub struct Client {
base_url: String,
api_key: String,
inner_client: reqwest::blocking::Client,
}
impl Client {
pub fn new(config: Config) -> Self {
Client {
base_url: config.base_url.unwrap_or(BASE_URL.to_string()),
api_key: config.api_key,
inner_client: reqwest::blocking::Client::builder()
.pool_idle_timeout(None)
.build()
.unwrap(),
}
}
fn build_request(parameters: HashMap<String, String>) -> String {
parameters
.iter()
.map(|(key, value)| format!("{}={}", key, value))
.collect::<Vec<String>>()
.join("&")
}
pub fn get<T: DeserializeOwned>(
&self,
endpoint: &'static str,
parameters: Option<HashMap<String, String>>,
) -> Result<T> {
let mut url: String = format!("{}{}", self.base_url, endpoint);
if let Some(p) = parameters {
let request = Self::build_request(p);
if !request.is_empty() {
url.push_str(format!("?{}", request).as_str());
}
}
let client = &self.inner_client;
let response = client
.get(url.as_str())
.header("X-DTMX-APIKEY", &self.api_key)
.send()?;
self.handle_response(response)
}
fn handle_response<T: DeserializeOwned>(&self, response: Response) -> Result<T> {
match response.status() {
StatusCode::OK => Ok(response.json::<T>()?),
StatusCode::INTERNAL_SERVER_ERROR => {
let mut response_text = String::new();
response.take(1000).read_to_string(&mut response_text)?;
Err(ErrorKind::InternalServerError(response_text).into())
}
StatusCode::UNAUTHORIZED => Err(ErrorKind::Unauthorized.into()),
StatusCode::BAD_REQUEST => {
let mut response_text = String::new();
response.take(1000).read_to_string(&mut response_text)?;
Err(ErrorKind::BadRequest(response_text).into())
}
status => Err(ErrorKind::UnexpectedStatusCode(status.as_u16()).into()),
}
}
}
error_chain! {
errors {
BadRequest(msg: String) {
description("Bad request")
display("Bad request: {}", msg)
}
Unauthorized {
description("Unauthorized")
display("Unauthorized")
}
InternalServerError(msg: String) {
description("Internal server error")
display("Internal server error: {}", msg)
}
UnexpectedStatusCode(status: u16) {
description("Unexpected status code")
display("Received unexpected status code: {}", status)
}
}
foreign_links {
ReqError(reqwest::Error);
InvalidHeaderError(reqwest::header::InvalidHeaderValue);
IoError(std::io::Error);
ParseFloatError(std::num::ParseFloatError);
UrlParserError(url::ParseError);
Json(serde_json::Error);
Tungstenite(tungstenite::Error);
TimestampError(std::time::SystemTimeError);
}
}