use reqwest::{blocking::Client, header};
use serde::de::DeserializeOwned;
use crate::{
helpers::response_handler::{parse_raw_response, parse_json},
responses::{response::ApiResponse, search::SearchResponse},
SearchQuery, Work, DataProvider,
};
use super::{query_models::{query::Query, request_type::QueryRequestType}, discovery_models::discovery::Discovery, journal_models::journal::Journal};
#[derive(Debug)]
pub struct Api {
key: String,
client: Client,
log_target: bool,
log_raw_response: bool,
}
impl Api {
pub fn discover<T>(
&self,
doi: T
) -> Result<ApiResponse<Discovery>, crate::errors::Error>
where
T: ToString + Clone
{
self.execute_query::<T, String, Discovery>(Query::Discovery(doi))
}
pub fn get_journal<T>(
&self,
id: T
) -> Result<ApiResponse<Journal>, crate::errors::Error>
where
T: ToString + Clone
{
self.execute_query::<T, String, Journal>(Query::Journals(id))
}
pub fn get_output<T>(
&self,
id: T
) -> Result<ApiResponse<Work>, crate::errors::Error>
where
T: ToString + Clone
{
self.execute_query::<T, String, Work>(Query::Outputs(id))
}
pub fn get_data_provider<T>(
&self,
id: T
) -> Result<ApiResponse<DataProvider>, crate::errors::Error>
where
T: ToString + Clone
{
self.execute_query::<T, String, DataProvider>(Query::DataProviders(id))
}
pub fn search_works<T1, T2>(
&self,
query: SearchQuery<T1, T2>
) -> Result<ApiResponse<SearchResponse<Work>>, crate::errors::Error>
where
T1: ToString + Clone,
T2: ToString + Clone,
{
self.execute_query(Query::SearchWorks(query))
}
pub fn search_data_providers<T1, T2>(
&self,
query: SearchQuery<T1, T2>
) -> Result<ApiResponse<SearchResponse<DataProvider>>, crate::errors::Error>
where
T1: ToString + Clone,
T2: ToString + Clone,
{
self.execute_query(Query::SearchDataProviders(query))
}
pub fn search_journals<T1, T2>(
&self,
query: SearchQuery<T1, T2>
) -> Result<ApiResponse<SearchResponse<Journal>>, crate::errors::Error>
where
T1: ToString + Clone,
T2: ToString + Clone,
{
self.execute_query(Query::SearchJournals(query))
}
pub fn search_outputs<T1, T2>(
&self,
query: SearchQuery<T1, T2>
) -> Result<ApiResponse<SearchResponse<Work>>, crate::errors::Error>
where
T1: ToString + Clone,
T2: ToString + Clone,
{
self.execute_query(Query::SearchOutputs(query))
}
pub fn paged_search<T1 , T2>(
&self, limit: i32,
offset: i32
) -> SearchQuery<T1, T2>
where
T1:ToString,
T2:ToString,
{
SearchQuery::paged(limit, offset)
}
pub fn log_target(self, log_target: bool) -> Self {
Self { key: self.key, client: self.client, log_target, log_raw_response: self.log_raw_response }
}
pub fn log_raw_response(self, log_raw_response: bool) -> Self {
Self { key: self.key, client: self.client, log_target: self.log_target, log_raw_response }
}
fn execute_query<T1, T2, T3>(
&self,
query: Query<T1, T2>
) -> Result<ApiResponse<T3>, crate::errors::Error>
where
T1: ToString + Clone,
T2: ToString + Clone,
T3: DeserializeOwned
{
let (req_type, query_uri, body) = query.parse_request();
let target = format!("https://api.core.ac.uk/v3/{}", query_uri);
if self.log_target {
println!("{}", query_uri);
}
let client_builer = match req_type {
QueryRequestType::Get => self.client.get(target),
QueryRequestType::Post => self.client.post(target),
};
let client_builer = client_builer.header(
header::AUTHORIZATION,
format!("Bearer {}", self.key.clone())
);
let client_builder = match (req_type, body) {
(QueryRequestType::Get, None) => client_builer,
(QueryRequestType::Get, Some(_)) => client_builer,
(QueryRequestType::Post, None) => client_builer,
(QueryRequestType::Post, Some(content)) => client_builer.body(content),
};
let response = match client_builder.send() {
Ok(r) => r,
Err(e) => return Err(crate::errors::Error::Request(e)),
};
let (data, rate_limit) = parse_raw_response(response)?;
if self.log_raw_response {
println!("{}", data);
}
Ok(ApiResponse {
ratelimit_remaining: rate_limit,
response: parse_json::<T3>(&data)?,
})
}
}
impl<T: Into<String>> From<T> for Api {
fn from(key: T) -> Self {
let client = reqwest::blocking::Client::new();
Api { key: key.into(), client, log_target: false, log_raw_response: false }
}
}