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)]
27pub struct Response {
29 pub response: reqwest::Response,
31 pub request: reqwest::Request,
33}
34
35pub 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 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 pub fn key_info(&self) -> GetKeyInfoRequest {
120 GetKeyInfoRequest::new(self)
121 }
122
123 pub fn global(&self) -> GetGlobalRequest {
128 GetGlobalRequest::new(self)
129 }
130
131 pub fn coins(&self) -> GetCoinsRequest {
136 GetCoinsRequest::new(self)
137 }
138
139 pub fn coin(&self, coin_id: &str) -> GetCoinRequest {
141 GetCoinRequest::new(self, coin_id)
142 }
143
144 pub fn twitter(&self, coin_id: &str) -> GetTwitterRequest {
147 GetTwitterRequest::new(self, coin_id)
148 }
149
150 pub fn coin_events(&self, coin_id: &str) -> GetCoinEventsRequest {
153 GetCoinEventsRequest::new(self, coin_id)
154 }
155
156 pub fn coin_exchanges(&self, coin_id: &str) -> GetCoinExchangesRequest {
159 GetCoinExchangesRequest::new(self, coin_id)
160 }
161
162 pub fn coin_markets(&self, coin_id: &str) -> GetCoinMarketsRequest {
164 GetCoinMarketsRequest::new(self, coin_id)
165 }
166
167 pub fn coin_ohlc_last_full_day(&self, coin_id: &str) -> GetCoinOHLCLastFullDayRequest {
170 GetCoinOHLCLastFullDayRequest::new(self, coin_id)
171 }
172
173 pub fn coin_ohlc_historical(&self, coin_id: &str) -> GetCoinOHLCHistoricalRequest {
176 GetCoinOHLCHistoricalRequest::new(self, coin_id)
177 }
178
179 pub fn coin_ohlc_today(&self, coin_id: &str) -> GetCoinOHLCTodayRequest {
182 GetCoinOHLCTodayRequest::new(self, coin_id)
183 }
184
185 pub fn person(&self, person_id: &str) -> GetPersonRequest {
191 GetPersonRequest::new(self, person_id)
192 }
193
194 pub fn tags(&self) -> GetTagsRequest {
200 GetTagsRequest::new(self)
201 }
202
203 pub fn tag(&self, tag_id: &str) -> GetTagRequest {
206 GetTagRequest::new(self, tag_id)
207 }
208
209 pub fn tickers(&self) -> GetTickersRequest {
214 GetTickersRequest::new(self)
215 }
216
217 pub fn ticker(&self, coin_id: &str) -> GetTickerRequest {
220 GetTickerRequest::new(self, coin_id)
221 }
222
223 pub fn historical_ticks(&self, coin_id: &str) -> GetHistoricalTicksRequest {
226 GetHistoricalTicksRequest::new(self, coin_id)
227 }
228
229 pub fn exchanges(&self) -> GetExchangesRequest {
234 GetExchangesRequest::new(self)
235 }
236
237 pub fn exchange(&self, exchange_id: &str) -> GetExchangeRequest {
240 GetExchangeRequest::new(self, exchange_id)
241 }
242
243 pub fn exchange_markets(&self, exchange_id: &str) -> GetExchangeMarketsRequest {
246 GetExchangeMarketsRequest::new(self, exchange_id)
247 }
248
249 pub fn search(&self, q: &str) -> GetSearchRequest {
254 GetSearchRequest::new(self, q)
255 }
256
257 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 pub fn contract_platforms(&self) -> GetContractPlatformsRequest {
272 GetContractPlatformsRequest::new(self)
273 }
274
275 pub fn contracts(&self, platform_id: &str) -> GetContractsRequest {
278 GetContractsRequest::new(self, platform_id)
279 }
280
281 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}