Skip to main content

datamaxi/
api.rs

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
10/// A trait that defines the required methods for interacting with the Datamaxi+ API.
11pub trait Datamaxi {
12    /// Creates a new instance of the implementing type using the provided API key.
13    fn new(api_key: String) -> Self;
14
15    /// Creates a new instance of the implementing type using the provided API key and base URL.
16    fn new_with_base_url(api_key: String, base_url: String) -> Self;
17}
18
19/// The configuration for the Datamaxi+ API client.
20pub struct Config {
21    /// The base URL for the API.
22    pub base_url: Option<String>,
23
24    /// The API key used for authentication.
25    pub api_key: String,
26}
27
28/// The client for interacting with the Datamaxi+ API.
29#[derive(Clone)]
30pub struct Client {
31    base_url: String,
32    api_key: String,
33    inner_client: reqwest::blocking::Client,
34}
35
36impl Client {
37    /// Creates a new instance of the `Client` struct with the provided configuration.
38    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    /// Builds a request string from a set of parameters.
50    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    /// Sends a GET request to the specified endpoint with optional parameters.
59    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    /// Processes the response from the API and returns the result.
84    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        /// Represents an error that occurs when a request to the API returns a bad request status.
106        BadRequest(msg: String) {
107            description("Bad request")
108            display("Bad request: {}", msg)
109        }
110
111        /// Represents an error that occurs when a request to the API returns an unauthorized status.
112        Unauthorized {
113            description("Unauthorized")
114            display("Unauthorized")
115        }
116
117        /// Represents an error that occurs when a request to the API returns an internal server error status.
118        InternalServerError(msg: String) {
119            description("Internal server error")
120            display("Internal server error: {}", msg)
121        }
122
123        /// Represents an error that occurs when a request to the API returns an unexpected status code.
124        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}