1use error_chain::error_chain;
2use reqwest::blocking::Response;
3use reqwest::StatusCode;
4use serde::de::DeserializeOwned;
5use std::collections::HashMap;
6use std::io::Read;
7
8const BASE_URL: &str = "https://api.datamaxiplus.com/api/v1";
9
10pub trait Datamaxi {
12 fn new(api_key: String) -> Self;
14
15 fn new_with_base_url(api_key: String, base_url: String) -> Self;
17}
18
19pub struct Config {
21 pub base_url: Option<String>,
23
24 pub api_key: String,
26}
27
28#[derive(Clone)]
30pub struct Client {
31 base_url: String,
32 api_key: String,
33 inner_client: reqwest::blocking::Client,
34}
35
36impl Client {
37 pub fn new(config: Config) -> Self {
39 Client {
40 base_url: config.base_url.unwrap_or(BASE_URL.to_string()),
41 api_key: config.api_key,
42 inner_client: reqwest::blocking::Client::builder()
43 .pool_idle_timeout(None)
44 .build()
45 .unwrap(),
46 }
47 }
48
49 fn build_request(parameters: HashMap<String, String>) -> String {
51 parameters
52 .iter()
53 .map(|(key, value)| format!("{}={}", key, value))
54 .collect::<Vec<String>>()
55 .join("&")
56 }
57
58 pub fn get<T: DeserializeOwned>(
60 &self,
61 endpoint: &'static str,
62 parameters: Option<HashMap<String, String>>,
63 ) -> Result<T> {
64 let mut url: String = format!("{}{}", self.base_url, endpoint);
65
66 if let Some(p) = parameters {
67 let request = Self::build_request(p);
68
69 if !request.is_empty() {
70 url.push_str(format!("?{}", request).as_str());
71 }
72 }
73
74 let client = &self.inner_client;
75 let response = client
76 .get(url.as_str())
77 .header("X-DTMX-APIKEY", &self.api_key)
78 .send()?;
79
80 self.handle_response(response)
81 }
82
83 fn handle_response<T: DeserializeOwned>(&self, response: Response) -> Result<T> {
85 match response.status() {
86 StatusCode::OK => Ok(response.json::<T>()?),
87 StatusCode::INTERNAL_SERVER_ERROR => {
88 let mut response_text = String::new();
89 response.take(1000).read_to_string(&mut response_text)?;
90 Err(ErrorKind::InternalServerError(response_text).into())
91 }
92 StatusCode::UNAUTHORIZED => Err(ErrorKind::Unauthorized.into()),
93 StatusCode::BAD_REQUEST => {
94 let mut response_text = String::new();
95 response.take(1000).read_to_string(&mut response_text)?;
96 Err(ErrorKind::BadRequest(response_text).into())
97 }
98 status => Err(ErrorKind::UnexpectedStatusCode(status.as_u16()).into()),
99 }
100 }
101}
102
103error_chain! {
104 errors {
105 BadRequest(msg: String) {
107 description("Bad request")
108 display("Bad request: {}", msg)
109 }
110
111 Unauthorized {
113 description("Unauthorized")
114 display("Unauthorized")
115 }
116
117 InternalServerError(msg: String) {
119 description("Internal server error")
120 display("Internal server error: {}", msg)
121 }
122
123 UnexpectedStatusCode(status: u16) {
125 description("Unexpected status code")
126 display("Received unexpected status code: {}", status)
127 }
128 }
129
130 foreign_links {
131 ReqError(reqwest::Error);
132 InvalidHeaderError(reqwest::header::InvalidHeaderValue);
133 IoError(std::io::Error);
134 ParseFloatError(std::num::ParseFloatError);
135 UrlParserError(url::ParseError);
136 Json(serde_json::Error);
137 Tungstenite(tungstenite::Error);
138 TimestampError(std::time::SystemTimeError);
139 }
140}