use std::time::Duration;
use serde::Serialize;
use serde::de::DeserializeOwned;
pub use crate::client_common::DEFAULT_BASE_URL;
use crate::client_common::{
cbr_endpoint_methods, configure_reqwest_builder, endpoint, normalize_base_url,
};
use crate::error::{CbrError, parse_json_body};
use crate::models::{
CategoryNewResponse, DataExResponse, DataNewResponse, DataResponse, Dataset,
DatasetDescription, DatasetsExResponse, MeasuresResponse, Publication, YearRange,
};
use crate::query::{
DataExQuery, DataNewQuery, DataQuery, dataset_id_query, publication_id_query, years_ex_query,
years_query,
};
use crate::types::{DatasetId, MeasureId, PublicationId};
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
macro_rules! impl_async_endpoint_method {
(
$doc:literal,
$name:ident,
($($arg_name:ident : $arg_ty:ty),* $(,)?),
$ret:ty,
$path:literal,
no_query
) => {
#[doc = $doc]
#[inline]
pub async fn $name(&self $(, $arg_name: $arg_ty)*) -> Result<$ret, CbrError> {
self.request_json($path).await
}
};
(
$doc:literal,
$name:ident,
($($arg_name:ident : $arg_ty:ty),* $(,)?),
$ret:ty,
$path:literal,
query($query:expr)
) => {
#[doc = $doc]
#[inline]
pub async fn $name(&self $(, $arg_name: $arg_ty)*) -> Result<$ret, CbrError> {
self.request_json_with_query($path, &$query).await
}
};
}
#[derive(Debug, Clone)]
pub struct CbrClientBuilder {
pub(crate) base_url: String,
pub(crate) timeout: Duration,
pub(crate) user_agent: Option<String>,
pub(crate) proxy_url: Option<String>,
pub(crate) use_system_proxy: bool,
}
impl Default for CbrClientBuilder {
fn default() -> Self {
Self {
base_url: DEFAULT_BASE_URL.to_owned(),
timeout: DEFAULT_TIMEOUT,
user_agent: None,
proxy_url: None,
use_system_proxy: false,
}
}
}
impl CbrClientBuilder {
#[must_use]
#[inline]
pub fn new() -> Self {
Self::default()
}
#[must_use]
#[inline]
pub fn base_url(mut self, base_url: impl Into<String>) -> Self {
self.base_url = normalize_base_url(base_url.into());
self
}
#[must_use]
#[inline]
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
#[must_use]
#[inline]
pub fn user_agent(mut self, user_agent: impl Into<String>) -> Self {
self.user_agent = Some(user_agent.into());
self
}
#[must_use]
#[inline]
pub fn proxy(mut self, proxy_url: impl Into<String>) -> Self {
self.proxy_url = Some(proxy_url.into());
self
}
#[must_use]
#[inline]
pub fn use_system_proxy(mut self, enabled: bool) -> Self {
self.use_system_proxy = enabled;
self
}
pub fn build(self) -> Result<CbrClient, CbrError> {
let builder = configure_reqwest_builder!(
reqwest::Client::builder(),
timeout = self.timeout,
use_system_proxy = self.use_system_proxy,
proxy_url = self.proxy_url.as_deref(),
user_agent = self.user_agent.as_deref()
);
let http = builder.build().map_err(CbrError::build)?;
Ok(CbrClient {
base_url: normalize_base_url(&self.base_url),
http,
})
}
#[cfg(feature = "blocking")]
#[inline]
pub fn build_blocking(self) -> Result<crate::blocking::BlockingCbrClient, CbrError> {
crate::blocking::BlockingCbrClient::from_builder(self)
}
}
#[derive(Debug, Clone)]
pub struct CbrClient {
base_url: String,
http: reqwest::Client,
}
impl CbrClient {
#[inline]
pub fn new() -> Result<Self, CbrError> {
Self::builder().build()
}
#[must_use]
#[inline]
pub fn builder() -> CbrClientBuilder {
CbrClientBuilder::new()
}
#[must_use]
#[inline]
pub fn base_url(&self) -> &str {
&self.base_url
}
#[inline]
pub async fn request_json<T>(&self, path: &str) -> Result<T, CbrError>
where
T: DeserializeOwned,
{
self.get_json(path).await
}
#[inline]
pub async fn request_json_with_query<T, Q>(&self, path: &str, query: &Q) -> Result<T, CbrError>
where
T: DeserializeOwned,
Q: Serialize + ?Sized,
{
self.get_json_with_query(path, query).await
}
cbr_endpoint_methods!(impl_async_endpoint_method);
async fn get_json<T>(&self, path: &str) -> Result<T, CbrError>
where
T: DeserializeOwned,
{
let response = self
.http
.get(endpoint(&self.base_url, path))
.send()
.await
.map_err(CbrError::transport)?;
let status = response.status();
let body = response.bytes().await.map_err(CbrError::transport)?;
parse_json_body(status, body.as_ref())
}
async fn get_json_with_query<T, Q>(&self, path: &str, query: &Q) -> Result<T, CbrError>
where
T: DeserializeOwned,
Q: Serialize + ?Sized,
{
let response = self
.http
.get(endpoint(&self.base_url, path))
.query(query)
.send()
.await
.map_err(CbrError::transport)?;
let status = response.status();
let body = response.bytes().await.map_err(CbrError::transport)?;
parse_json_body(status, body.as_ref())
}
}