use crate::{response_models::*, param_models::*, attribute_models::*, format_models::*, endpoints::*};
use std::error::Error;
use reqwest::blocking::Client;
use std::env;
pub const DEFAULT_BASE_URL: &str = "https://www.loc.gov/";
pub struct ApiClient {
base_url: String,
client: Client,
}
impl ApiClient {
pub fn new() -> Self {
let base_url = env::var("LOC_API_BASE_URL").unwrap_or_else(|_| DEFAULT_BASE_URL.to_string());
let client = Client::new();
ApiClient { base_url, client }
}
pub fn search(
&self,
query: &str,
include_collections: bool,
attributes: Option<AttributesSelect>,
filters: Option<FacetReq>,
per_page: Option<u32>,
page: Option<u32>,
sort: Option<SortField>,
) -> Result<(SearchResultResponse, String), Box<dyn Error>> {
let common_params = CommonParams {
format: Format::default().into(),
attributes,
query: query.to_string().replace(" ", "+").into(),
filter: filters,
per_page,
page,
sort,
};
let search_params = SearchParams {
common: common_params,
include_collections,
};
let endpoint = Endpoints::Search(search_params);
let url = endpoint.to_url()?;
let final_url = self.replace_base_url(&url)?;
let response = self.client.get(&final_url).send()?.error_for_status()?;
let json = response.json::<SearchResultResponse>()?;
Ok((json, final_url))
}
pub fn get_item(
&self,
item_id: &str,
attributes: Option<ItemAttributes>,
) -> Result<(ItemResponse, String), Box<dyn Error>> {
let item_params = ItemParams {
format: Some(Format::default()),
attributes,
};
let endpoint = Endpoints::Item {
item_id: item_id.to_string(),
params: item_params,
};
let url = endpoint.to_url()?;
let final_url = self.replace_base_url(&url)?;
let response = self.client.get(&final_url).send()?.error_for_status()?;
let json = response.json::<ItemResponse>()?;
Ok((json, final_url))
}
pub fn get_format(
&self,
format_type: MediaType,
query: Option<&str>,
attributes: Option<AttributesSelect>,
filters: Option<FacetReq>,
per_page: Option<u32>,
page: Option<u32>,
sort: Option<SortField>,
) -> Result<(FormatResponse, String), Box<dyn Error>> {
let query = if let Some(q) = query { Some(q.replace(" ", "+")) } else { None };
let common_params = CommonParams {
format: Some(Format::default()),
attributes,
query,
filter: filters,
per_page,
page,
sort,
};
let endpoint = Endpoints::Format {
format: format_type,
params: common_params,
};
let url = endpoint.to_url()?;
let final_url = self.replace_base_url(&url)?;
let response = self.client.get(&final_url).send()?.error_for_status()?;
let json = response.json::<FormatResponse>()?;
Ok((json, final_url))
}
pub fn get_collection(
&self,
collection_name: &str,
query: Option<&str>,
attributes: Option<AttributesSelect>,
filters: Option<FacetReq>,
per_page: Option<u32>,
page: Option<u32>,
sort: Option<SortField>,
) -> Result<(CollectionResponse, String), Box<dyn Error>> {
let query = if let Some(q) = query { Some(q.replace(" ", "+")) } else { None };
let common_params = CommonParams {
format: Some(Format::default()),
attributes,
query,
filter: filters,
per_page,
page,
sort,
};
let endpoint = Endpoints::Collection {
name: collection_name.to_string().replace(" ", "-").replace("_", "-"),
params: common_params,
};
let url = endpoint.to_url()?;
let final_url = self.replace_base_url(&url)?;
let response = self.client.get(&final_url).send()?.error_for_status()?;
let json = response.json::<CollectionResponse>()?;
Ok((json, final_url))
}
pub fn get_collections(
&self,
query: Option<&str>,
attributes: Option<AttributesSelect>,
filters: Option<FacetReq>,
per_page: Option<u32>,
page: Option<u32>,
sort: Option<SortField>,
) -> Result<(CollectionsResponse, String), Box<dyn Error>> {
let query = if let Some(q) = query { Some(q.replace(" ", "+")) } else { None };
let common_params = CommonParams {
format: Some(Format::default()),
attributes,
query,
filter: filters,
per_page,
page,
sort,
};
let endpoint = Endpoints::Collections(common_params);
let url = endpoint.to_url()?;
let final_url = self.replace_base_url(&url)?;
let response = self.client.get(&final_url).send()?.error_for_status()?;
let json = response.json::<CollectionsResponse>()?;
Ok((json, final_url))
}
fn replace_base_url(&self, url: &str) -> Result<String, Box<dyn Error>> {
let default_base = "https://www.loc.gov";
if url.starts_with(default_base) {
let suffix = &url[default_base.len()..];
Ok(format!("{}{}", self.base_url, suffix))
} else {
Err(format!("URL does not start with the expected base URL: {}", default_base).into())
}
}
}