1use std::time::Duration;
2
3use serde::Serialize;
4use serde::de::DeserializeOwned;
5
6pub use crate::client_common::DEFAULT_BASE_URL;
7use crate::client_common::{
8 cbr_endpoint_methods, configure_reqwest_builder, endpoint, normalize_base_url,
9};
10use crate::error::{CbrError, parse_json_body};
11use crate::models::{
12 CategoryNewResponse, DataExResponse, DataNewResponse, DataResponse, Dataset,
13 DatasetDescription, DatasetsExResponse, MeasuresResponse, Publication, YearRange,
14};
15use crate::query::{
16 DataExQuery, DataNewQuery, DataQuery, dataset_id_query, publication_id_query, years_ex_query,
17 years_query,
18};
19use crate::types::{DatasetId, MeasureId, PublicationId};
20
21const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
22
23macro_rules! impl_async_endpoint_method {
24 (
25 $doc:literal,
26 $name:ident,
27 ($($arg_name:ident : $arg_ty:ty),* $(,)?),
28 $ret:ty,
29 $path:literal,
30 no_query
31 ) => {
32 #[doc = $doc]
33 #[inline]
34 pub async fn $name(&self $(, $arg_name: $arg_ty)*) -> Result<$ret, CbrError> {
35 self.request_json($path).await
36 }
37 };
38 (
39 $doc:literal,
40 $name:ident,
41 ($($arg_name:ident : $arg_ty:ty),* $(,)?),
42 $ret:ty,
43 $path:literal,
44 query($query:expr)
45 ) => {
46 #[doc = $doc]
47 #[inline]
48 pub async fn $name(&self $(, $arg_name: $arg_ty)*) -> Result<$ret, CbrError> {
49 self.request_json_with_query($path, &$query).await
50 }
51 };
52}
53
54#[derive(Debug, Clone)]
56pub struct CbrClientBuilder {
57 pub(crate) base_url: String,
58 pub(crate) timeout: Duration,
59 pub(crate) user_agent: Option<String>,
60 pub(crate) proxy_url: Option<String>,
61 pub(crate) use_system_proxy: bool,
62}
63
64impl Default for CbrClientBuilder {
65 fn default() -> Self {
66 Self {
67 base_url: DEFAULT_BASE_URL.to_owned(),
68 timeout: DEFAULT_TIMEOUT,
69 user_agent: None,
70 proxy_url: None,
71 use_system_proxy: false,
72 }
73 }
74}
75
76impl CbrClientBuilder {
77 #[must_use]
79 #[inline]
80 pub fn new() -> Self {
81 Self::default()
82 }
83
84 #[must_use]
88 #[inline]
89 pub fn base_url(mut self, base_url: impl Into<String>) -> Self {
90 self.base_url = normalize_base_url(base_url.into());
91 self
92 }
93
94 #[must_use]
96 #[inline]
97 pub fn timeout(mut self, timeout: Duration) -> Self {
98 self.timeout = timeout;
99 self
100 }
101
102 #[must_use]
104 #[inline]
105 pub fn user_agent(mut self, user_agent: impl Into<String>) -> Self {
106 self.user_agent = Some(user_agent.into());
107 self
108 }
109
110 #[must_use]
116 #[inline]
117 pub fn proxy(mut self, proxy_url: impl Into<String>) -> Self {
118 self.proxy_url = Some(proxy_url.into());
119 self
120 }
121
122 #[must_use]
127 #[inline]
128 pub fn use_system_proxy(mut self, enabled: bool) -> Self {
129 self.use_system_proxy = enabled;
130 self
131 }
132
133 pub fn build(self) -> Result<CbrClient, CbrError> {
135 let builder = configure_reqwest_builder!(
137 reqwest::Client::builder(),
138 timeout = self.timeout,
139 use_system_proxy = self.use_system_proxy,
140 proxy_url = self.proxy_url.as_deref(),
141 user_agent = self.user_agent.as_deref()
142 );
143
144 let http = builder.build().map_err(CbrError::build)?;
145 Ok(CbrClient {
146 base_url: normalize_base_url(&self.base_url),
147 http,
148 })
149 }
150
151 #[cfg(feature = "blocking")]
155 #[inline]
156 pub fn build_blocking(self) -> Result<crate::blocking::BlockingCbrClient, CbrError> {
157 crate::blocking::BlockingCbrClient::from_builder(self)
158 }
159}
160
161#[derive(Debug, Clone)]
163pub struct CbrClient {
164 base_url: String,
165 http: reqwest::Client,
166}
167
168impl CbrClient {
169 #[inline]
171 pub fn new() -> Result<Self, CbrError> {
172 Self::builder().build()
173 }
174
175 #[must_use]
177 #[inline]
178 pub fn builder() -> CbrClientBuilder {
179 CbrClientBuilder::new()
180 }
181
182 #[must_use]
184 #[inline]
185 pub fn base_url(&self) -> &str {
186 &self.base_url
187 }
188
189 #[inline]
193 pub async fn request_json<T>(&self, path: &str) -> Result<T, CbrError>
194 where
195 T: DeserializeOwned,
196 {
197 self.get_json(path).await
198 }
199
200 #[inline]
204 pub async fn request_json_with_query<T, Q>(&self, path: &str, query: &Q) -> Result<T, CbrError>
205 where
206 T: DeserializeOwned,
207 Q: Serialize + ?Sized,
208 {
209 self.get_json_with_query(path, query).await
210 }
211
212 cbr_endpoint_methods!(impl_async_endpoint_method);
213
214 async fn get_json<T>(&self, path: &str) -> Result<T, CbrError>
215 where
216 T: DeserializeOwned,
217 {
218 let response = self
219 .http
220 .get(endpoint(&self.base_url, path))
221 .send()
222 .await
223 .map_err(CbrError::transport)?;
224 let status = response.status();
225 let body = response.bytes().await.map_err(CbrError::transport)?;
226 parse_json_body(status, body.as_ref())
227 }
228
229 async fn get_json_with_query<T, Q>(&self, path: &str, query: &Q) -> Result<T, CbrError>
230 where
231 T: DeserializeOwned,
232 Q: Serialize + ?Sized,
233 {
234 let response = self
235 .http
236 .get(endpoint(&self.base_url, path))
237 .query(query)
238 .send()
239 .await
240 .map_err(CbrError::transport)?;
241 let status = response.status();
242 let body = response.bytes().await.map_err(CbrError::transport)?;
243 parse_json_body(status, body.as_ref())
244 }
245}