use crate::client::OpenLibraryClient;
use crate::error::{Error, Result};
use crate::models::search::{
AuthorDoc, AuthorSearchParams, BookDoc, InsideDoc, ListDoc, SearchParams, SearchResponse,
SubjectDoc,
};
use crate::validation::{validate_limit, validate_search_query};
impl OpenLibraryClient {
pub async fn search(&self, params: SearchParams) -> Result<SearchResponse<BookDoc>> {
let q = params.q.as_deref().unwrap_or_default();
let title = params.title.as_deref().unwrap_or_default();
let author = params.author.as_deref().unwrap_or_default();
let isbn = params.isbn.as_deref().unwrap_or_default();
let subject = params.subject.as_deref().unwrap_or_default();
if q.is_empty() && title.is_empty() && author.is_empty() && isbn.is_empty() && subject.is_empty() {
return Err(Error::InvalidInput(
"at least one of q, title, author, isbn, or subject must be provided".into(),
));
}
if !q.is_empty() {
validate_search_query(q)?;
}
if let Some(ref limit) = params.limit {
validate_limit(*limit)?;
}
let mut url = self.base_url.join("search.json")?;
{
let mut qp = url.query_pairs_mut();
if !q.is_empty() { qp.append_pair("q", q); }
if !title.is_empty() { qp.append_pair("title", title); }
if !author.is_empty() { qp.append_pair("author", author); }
if !isbn.is_empty() { qp.append_pair("isbn", isbn); }
if !subject.is_empty() { qp.append_pair("subject", subject); }
if let Some(v) = params.place.as_deref() { qp.append_pair("place", v); }
if let Some(v) = params.person.as_deref() { qp.append_pair("person", v); }
if let Some(v) = params.language.as_deref() { qp.append_pair("language", v); }
if let Some(v) = params.publisher.as_deref() { qp.append_pair("publisher", v); }
if let Some(v) = params.page { qp.append_pair("page", &v.to_string()); }
if let Some(v) = params.limit { qp.append_pair("limit", &v.to_string()); }
if let Some(v) = params.offset { qp.append_pair("offset", &v.to_string()); }
if let Some(v) = params.sort.as_deref() { qp.append_pair("sort", v); }
if let Some(v) = params.fields.as_deref() { qp.append_pair("fields", v); }
if let Some(v) = params.lang.as_deref() { qp.append_pair("lang", v); }
}
self.get_json(url).await
}
pub async fn search_authors(
&self,
params: AuthorSearchParams,
) -> Result<SearchResponse<AuthorDoc>> {
let q = params.q.as_deref().unwrap_or_default();
if q.is_empty() {
return Err(Error::InvalidInput("author search query must not be empty".into()));
}
validate_search_query(q)?;
if let Some(ref limit) = params.limit {
validate_limit(*limit)?;
}
let mut url = self.base_url.join("search/authors.json")?;
{
let mut qp = url.query_pairs_mut();
qp.append_pair("q", q);
if let Some(v) = params.limit { qp.append_pair("limit", &v.to_string()); }
if let Some(v) = params.offset { qp.append_pair("offset", &v.to_string()); }
}
self.get_json(url).await
}
pub async fn search_subjects(&self, query: &str) -> Result<SearchResponse<SubjectDoc>> {
validate_search_query(query)?;
let mut url = self.base_url.join("search/subjects.json")?;
url.query_pairs_mut().append_pair("q", query);
self.get_json(url).await
}
pub async fn search_lists(&self, query: &str, limit: Option<u32>) -> Result<SearchResponse<ListDoc>> {
validate_search_query(query)?;
if let Some(l) = limit { validate_limit(l)?; }
let mut url = self.base_url.join("search/lists.json")?;
{
let mut qp = url.query_pairs_mut();
qp.append_pair("q", query);
if let Some(l) = limit { qp.append_pair("limit", &l.to_string()); }
}
self.get_json(url).await
}
pub async fn search_inside(
&self,
query: &str,
limit: Option<u32>,
) -> Result<SearchResponse<InsideDoc>> {
validate_search_query(query)?;
if let Some(l) = limit { validate_limit(l)?; }
let mut url = self.base_url.join("search/inside.json")?;
{
let mut qp = url.query_pairs_mut();
qp.append_pair("q", query);
if let Some(l) = limit { qp.append_pair("limit", &l.to_string()); }
}
self.get_json(url).await
}
}