use crate::{
errors::{AppError, DeserializeJsonSnafu, HttpSnafu},
models::{GoogleApiError, VolumeResponse},
queries::VolumeQuery,
};
use snafu::prelude::*;
pub mod errors;
pub mod models;
pub mod queries;
const GOOGLE_BOOKS_BASE_URL: &str = "https://www.googleapis.com";
#[derive(Clone)]
pub struct GoogleBooks {
pub client: reqwest::Client,
pub api_key: Option<String>,
}
impl Default for GoogleBooks {
fn default() -> Self {
Self::new(None)
}
}
impl GoogleBooks {
pub fn new(api_key: Option<String>) -> Self {
Self {
client: reqwest::Client::new(),
api_key,
}
}
pub async fn search(&self, query: VolumeQuery) -> Result<VolumeResponse, AppError> {
let response = reqwest::get(query.build_url(GOOGLE_BOOKS_BASE_URL, self.api_key.clone()))
.await
.context(HttpSnafu)?;
if !response.status().is_success() {
let error_body: GoogleApiError = response.json().await.context(DeserializeJsonSnafu)?;
if error_body.error.code == 429 {
return Err(AppError::RateLimitExceeded {
message: error_body.error.message,
});
}
return Err(AppError::GoogleApi {
code: error_body.error.code,
message: error_body.error.message,
reason: error_body
.error
.errors
.and_then(|e| e.first().map(|i| i.reason.clone())),
});
}
let result = response
.json::<VolumeResponse>()
.await
.context(DeserializeJsonSnafu)?;
Ok(result)
}
pub async fn search_by_id(id: impl Into<String>) -> Result<VolumeResponse, AppError> {
let response = reqwest::get(&format!(
"{}/books/v1/volumes/{}",
GOOGLE_BOOKS_BASE_URL,
id.into()
))
.await
.context(HttpSnafu)?;
if !response.status().is_success() {
let error_body: GoogleApiError = response.json().await.context(DeserializeJsonSnafu)?;
if error_body.error.code == 429 {
return Err(AppError::RateLimitExceeded {
message: error_body.error.message,
});
}
return Err(AppError::GoogleApi {
code: error_body.error.code,
message: error_body.error.message,
reason: error_body
.error
.errors
.and_then(|e| e.first().map(|i| i.reason.clone())),
});
}
let result = response
.json::<VolumeResponse>()
.await
.context(DeserializeJsonSnafu)?;
Ok(result)
}
}