coinpaprika_api/
client.rs

1use crate::changelog::GetChangelogRequest;
2use crate::coins::{
3    GetCoinEventsRequest, GetCoinExchangesRequest, GetCoinMarketsRequest,
4    GetCoinOHLCHistoricalRequest, GetCoinOHLCLastFullDayRequest, GetCoinOHLCTodayRequest,
5    GetCoinRequest, GetCoinsRequest, GetTwitterRequest,
6};
7use crate::contracts::{GetContractPlatformsRequest, GetContractsRequest};
8use crate::error::Error;
9use crate::exchanges::{GetExchangeMarketsRequest, GetExchangeRequest, GetExchangesRequest};
10use crate::global::GetGlobalRequest;
11use crate::key::GetKeyInfoRequest;
12use crate::people::GetPersonRequest;
13use crate::tags::{GetTagRequest, GetTagsRequest};
14use crate::tickers::{GetHistoricalTicksRequest, GetTickerRequest, GetTickersRequest};
15use crate::tools::{GetPriceConversionRequest, GetSearchRequest};
16use reqwest::StatusCode;
17use reqwest_middleware::{
18    ClientBuilder, ClientWithMiddleware, Error as ReqwestMiddlewareError, RequestBuilder,
19};
20use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
21
22static DEFAULT_USER_AGENT: &str = "coinpaprika-api-rust-client";
23static API_URL: &str = "https://api.coinpaprika.com/v1/";
24static API_URL_PRO: &str = "https://api-pro.coinpaprika.com/v1/";
25
26#[derive(Debug)]
27/// Response helper struct
28pub struct Response {
29    /// Http response
30    pub response: reqwest::Response,
31    /// Http request
32    pub request: reqwest::Request,
33}
34
35/// Client struct used for connecting with coinpaprika.com
36pub struct Client {
37    pub client: ClientWithMiddleware,
38    pub api_url: &'static str,
39    api_key: Option<String>,
40    user_agent: &'static str,
41}
42
43impl Client {
44    pub fn new() -> Self {
45        Client {
46            client: ClientBuilder::new(reqwest::Client::new())
47                .with(RetryTransientMiddleware::new_with_policy(
48                    ExponentialBackoff::builder().build_with_max_retries(3),
49                ))
50                .build(),
51            api_url: API_URL,
52            api_key: None,
53            user_agent: DEFAULT_USER_AGENT,
54        }
55    }
56
57    /// Function to create Client with API Key
58    pub fn with_key(key: &str) -> Self {
59        Client {
60            client: ClientBuilder::new(reqwest::Client::new())
61                .with(RetryTransientMiddleware::new_with_policy(
62                    ExponentialBackoff::builder().build_with_max_retries(3),
63                ))
64                .build(),
65            api_url: API_URL_PRO,
66            api_key: Some(String::from(key)),
67            user_agent: DEFAULT_USER_AGENT,
68        }
69    }
70
71    pub async fn request(&self, request: RequestBuilder) -> Result<Response, Error> {
72        let mut request = request.header("User-Agent", self.user_agent);
73
74        if let Some(api_key) = &self.api_key {
75            request = request.header("Authorization", api_key);
76        }
77
78        let request = request.build()?;
79
80        let response = self
81            .client
82            .execute(request.try_clone().expect(
83                "Error can remain unhandled because we're not using streams, which are the try_clone fail condition",
84            ))
85            .await;
86
87        match &response {
88            Ok(response) => match response.status() {
89                StatusCode::BAD_REQUEST => return Err(Error::InvalidRequestError),
90                StatusCode::PAYMENT_REQUIRED => return Err(Error::InsufficientPlan),
91                StatusCode::FORBIDDEN => return Err(Error::InvalidApiKey),
92                StatusCode::NOT_FOUND => return Err(Error::InvalidParameter),
93                StatusCode::TOO_MANY_REQUESTS => return Err(Error::RateLimitError),
94                StatusCode::INTERNAL_SERVER_ERROR => return Err(Error::InternalServerError),
95                _ => {}
96            },
97            Err(err) => match err {
98                ReqwestMiddlewareError::Middleware(_err) => {
99                    return Err(Error::ApiConnectionError);
100                }
101                ReqwestMiddlewareError::Reqwest(err) => {
102                    if err.is_connect() || err.is_timeout() {
103                        return Err(Error::ApiConnectionError);
104                    }
105                }
106            },
107        };
108
109        Ok(Response {
110            response: response?,
111            request,
112        })
113    }
114
115    //
116    // Key
117    //
118    /// Call to [/key/info](https://api.coinpaprika.com/#tag/Key/paths/~1key~1info/get)
119    pub fn key_info(&self) -> GetKeyInfoRequest {
120        GetKeyInfoRequest::new(self)
121    }
122
123    //
124    // Global
125    //
126    /// Call to [/global](https://api.coinpaprika.com/#tag/Global/paths/~1global/get)
127    pub fn global(&self) -> GetGlobalRequest {
128        GetGlobalRequest::new(self)
129    }
130
131    //
132    // Coins
133    //
134    /// Call to [/coins](https://api.coinpaprika.com/#tag/Coins/paths/~1coins/get)
135    pub fn coins(&self) -> GetCoinsRequest {
136        GetCoinsRequest::new(self)
137    }
138
139    /// Call to [/coins/{coin_id}](https://api.coinpaprika.com/#tag/Coins/operation/getCoinById)
140    pub fn coin(&self, coin_id: &str) -> GetCoinRequest {
141        GetCoinRequest::new(self, coin_id)
142    }
143
144    /// Call to
145    /// [/coins/{coin_id}/twitter](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1twitter/get)
146    pub fn twitter(&self, coin_id: &str) -> GetTwitterRequest {
147        GetTwitterRequest::new(self, coin_id)
148    }
149
150    /// Call to
151    /// [/coins/{coin_id}/events](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1events/get)
152    pub fn coin_events(&self, coin_id: &str) -> GetCoinEventsRequest {
153        GetCoinEventsRequest::new(self, coin_id)
154    }
155
156    /// Call to
157    /// [/coins/{coin_id}/exchanges](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1exchanges/get)
158    pub fn coin_exchanges(&self, coin_id: &str) -> GetCoinExchangesRequest {
159        GetCoinExchangesRequest::new(self, coin_id)
160    }
161
162    /// Call to [/coins/{coin_id}/markets](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1markets/get)
163    pub fn coin_markets(&self, coin_id: &str) -> GetCoinMarketsRequest {
164        GetCoinMarketsRequest::new(self, coin_id)
165    }
166
167    /// Call to
168    /// [/coins/{coin_id}/ohlcv/latest](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1ohlcv~1latest~1/get)
169    pub fn coin_ohlc_last_full_day(&self, coin_id: &str) -> GetCoinOHLCLastFullDayRequest {
170        GetCoinOHLCLastFullDayRequest::new(self, coin_id)
171    }
172
173    /// Call to
174    /// [/coins/{coin_id}/ohlcv/historical](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1ohlcv~1historical/get)
175    pub fn coin_ohlc_historical(&self, coin_id: &str) -> GetCoinOHLCHistoricalRequest {
176        GetCoinOHLCHistoricalRequest::new(self, coin_id)
177    }
178
179    /// Call to
180    /// [/coins/{coin_id}/ohlcv/today](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1ohlcv~1today~1/get)
181    pub fn coin_ohlc_today(&self, coin_id: &str) -> GetCoinOHLCTodayRequest {
182        GetCoinOHLCTodayRequest::new(self, coin_id)
183    }
184
185    //
186    // People
187    //
188    /// Call to
189    /// [/people/{person_id}](https://api.coinpaprika.com/#tag/People/operation/getPeopleById)
190    pub fn person(&self, person_id: &str) -> GetPersonRequest {
191        GetPersonRequest::new(self, person_id)
192    }
193
194    //
195    // Tags
196    //
197    /// Call to
198    /// [/tags](https://api.coinpaprika.com/#tag/Tags/paths/~1tags/get)
199    pub fn tags(&self) -> GetTagsRequest {
200        GetTagsRequest::new(self)
201    }
202
203    /// Call to
204    /// [/tags/{tag_id}](https://api.coinpaprika.com/#tag/Tags/paths/~1tags~1%7Btag_id%7D/get)
205    pub fn tag(&self, tag_id: &str) -> GetTagRequest {
206        GetTagRequest::new(self, tag_id)
207    }
208
209    //
210    // Tickers
211    //
212    /// Call to [/tickers](https://api.coinpaprika.com/#tag/Tickers/operation/getTickers)
213    pub fn tickers(&self) -> GetTickersRequest {
214        GetTickersRequest::new(self)
215    }
216
217    /// Call to
218    /// [/ticker/{coin_id}](https://api.coinpaprika.com/#tag/Tickers/operation/getTickersById)
219    pub fn ticker(&self, coin_id: &str) -> GetTickerRequest {
220        GetTickerRequest::new(self, coin_id)
221    }
222
223    /// Call to
224    /// [/ticker/{coin_id}/historical](https://api.coinpaprika.com/#tag/Tickers/operation/getTickersHistoricalById)
225    pub fn historical_ticks(&self, coin_id: &str) -> GetHistoricalTicksRequest {
226        GetHistoricalTicksRequest::new(self, coin_id)
227    }
228
229    //
230    // Exchanges
231    //
232    /// Call to [/exchanges](https://api.coinpaprika.com/#tag/Exchanges/operation/getExchanges)
233    pub fn exchanges(&self) -> GetExchangesRequest {
234        GetExchangesRequest::new(self)
235    }
236
237    /// Call to
238    /// [/exchanges/{exchange_id}](https://api.coinpaprika.com/#tag/Exchanges/operation/getExchangeByID)
239    pub fn exchange(&self, exchange_id: &str) -> GetExchangeRequest {
240        GetExchangeRequest::new(self, exchange_id)
241    }
242
243    /// Call to
244    /// [/exchanges/{exchange_id}/markets](https://api.coinpaprika.com/#tag/Exchanges/paths/~1exchanges~1%7Bexchange_id%7D~1markets/get)
245    pub fn exchange_markets(&self, exchange_id: &str) -> GetExchangeMarketsRequest {
246        GetExchangeMarketsRequest::new(self, exchange_id)
247    }
248
249    //
250    // Tools
251    //
252    /// Call to [/search](https://api.coinpaprika.com/#tag/Tools/paths/~1search/get)
253    pub fn search(&self, q: &str) -> GetSearchRequest {
254        GetSearchRequest::new(self, q)
255    }
256
257    /// Call to
258    /// [/price-converter](https://api.coinpaprika.com/#tag/Tools/paths/~1price-converter/get)
259    pub fn price_convert(
260        &self,
261        base_currency_id: &str,
262        quote_currency_id: &str,
263    ) -> GetPriceConversionRequest {
264        GetPriceConversionRequest::new(self, base_currency_id, quote_currency_id)
265    }
266
267    //
268    // Contracts
269    //
270    /// Call to [/contracts](https://api.coinpaprika.com/#tag/Contracts/operation/getPlatforms)
271    pub fn contract_platforms(&self) -> GetContractPlatformsRequest {
272        GetContractPlatformsRequest::new(self)
273    }
274
275    /// Call to
276    /// [/contracts/{platform_id}](https://api.coinpaprika.com/#tag/Contracts/operation/getContracts)
277    pub fn contracts(&self, platform_id: &str) -> GetContractsRequest {
278        GetContractsRequest::new(self, platform_id)
279    }
280
281    //
282    // Changelog
283    //
284    /// Call to [/changelog/ids](https://api.coinpaprika.com/#tag/Changelog/operation/getChangelogIDs)
285    pub fn changelog(&self, page: i32) -> GetChangelogRequest {
286        GetChangelogRequest::new(self, page)
287    }
288}
289
290impl Default for Client {
291    fn default() -> Self {
292        Self::new()
293    }
294}