1use crate::{
2 errors::{AppError, DeserializeJsonSnafu, HttpSnafu},
3 models::{GoogleApiError, VolumeResponse},
4 queries::VolumeQuery,
5};
6use snafu::prelude::*;
7
8pub mod errors;
9pub mod models;
10pub mod queries;
11
12const GOOGLE_BOOKS_BASE_URL: &str = "https://www.googleapis.com";
14
15#[derive(Clone)]
17pub struct GoogleBooks {
18 pub client: reqwest::Client,
19 pub api_key: Option<String>,
20}
21
22impl Default for GoogleBooks {
23 fn default() -> Self {
24 Self::new(None)
25 }
26}
27
28impl GoogleBooks {
29 pub fn new(api_key: Option<String>) -> Self {
31 Self {
32 client: reqwest::Client::new(),
33 api_key,
34 }
35 }
36
37 pub async fn search(&self, query: VolumeQuery) -> Result<VolumeResponse, AppError> {
51 let response = reqwest::get(query.build_url(GOOGLE_BOOKS_BASE_URL, self.api_key.clone()))
52 .await
53 .context(HttpSnafu)?;
54
55 if !response.status().is_success() {
56 let error_body: GoogleApiError = response.json().await.context(DeserializeJsonSnafu)?;
57
58 if error_body.error.code == 429 {
59 return Err(AppError::RateLimitExceeded {
60 message: error_body.error.message,
61 });
62 }
63
64 return Err(AppError::GoogleApi {
65 code: error_body.error.code,
66 message: error_body.error.message,
67 reason: error_body
68 .error
69 .errors
70 .and_then(|e| e.first().map(|i| i.reason.clone())),
71 });
72 }
73
74 let result = response
75 .json::<VolumeResponse>()
76 .await
77 .context(DeserializeJsonSnafu)?;
78 Ok(result)
79 }
80
81 pub async fn search_by_id(id: impl Into<String>) -> Result<VolumeResponse, AppError> {
93 let response = reqwest::get(&format!(
94 "{}/books/v1/volumes/{}",
95 GOOGLE_BOOKS_BASE_URL,
96 id.into()
97 ))
98 .await
99 .context(HttpSnafu)?;
100
101 if !response.status().is_success() {
102 let error_body: GoogleApiError = response.json().await.context(DeserializeJsonSnafu)?;
103
104 if error_body.error.code == 429 {
105 return Err(AppError::RateLimitExceeded {
106 message: error_body.error.message,
107 });
108 }
109
110 return Err(AppError::GoogleApi {
111 code: error_body.error.code,
112 message: error_body.error.message,
113 reason: error_body
114 .error
115 .errors
116 .and_then(|e| e.first().map(|i| i.reason.clone())),
117 });
118 }
119
120 let result = response
121 .json::<VolumeResponse>()
122 .await
123 .context(DeserializeJsonSnafu)?;
124
125 Ok(result)
126 }
127}