open_library/clients/
books.rs1use crate::clients::handle;
2use crate::models::books::{BibliographyKey, Book};
3use crate::models::identifiers::{
4 Identifier, InternationalStandardBookNumber, OpenLibraryIdentifier,
5};
6use crate::models::OpenLibraryModel;
7use crate::OpenLibraryError;
8use reqwest::Client;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11use url::Url;
12
13#[derive(Clone)]
14pub struct BooksClient {
15 client: Client,
16 host: Url,
17}
18
19type BookSearchResponse = HashMap<BibliographyKey, Book>;
20impl OpenLibraryModel for BookSearchResponse {}
21
22impl BooksClient {
23 pub fn new(client: &Client, host: &Url) -> Self {
24 Self {
25 client: client.clone(),
26 host: host.clone(),
27 }
28 }
29
30 pub async fn by_isbn(
31 &self,
32 isbn: InternationalStandardBookNumber,
33 ) -> Result<Book, OpenLibraryError> {
34 let url = self
35 .host
36 .join(format!("/isbn/{}.json", isbn.value()).as_str())?;
37
38 handle(self.client.get(url)).await
39 }
40
41 pub async fn get(&self, identifier: OpenLibraryIdentifier) -> Result<Book, OpenLibraryError> {
42 let url = self
43 .host
44 .join(format!("/books/{}.json", identifier.value()).as_str())?;
45
46 handle(self.client.get(url)).await
47 }
48
49 pub async fn search<'a, T: Into<&'a Vec<BibliographyKey>>>(
50 &self,
51 identifiers: T,
52 ) -> Result<BookSearchResponse, OpenLibraryError> {
53 let ids_filter = identifiers
54 .into()
55 .iter()
56 .map(|id| id.to_string())
57 .collect::<Vec<String>>()
58 .join(",");
59
60 handle(self.client.get(self.host.join("/api/books")?).query(&[
61 (QueryParameters::BibliographyKeys, &ids_filter),
62 (QueryParameters::Format, &String::from("json")),
63 (QueryParameters::JavascriptCommand, &String::from("data")),
64 ]))
65 .await
66 }
67}
68
69#[derive(Deserialize, Serialize)]
70enum QueryParameters {
71 #[serde(rename = "bibkeys")]
72 BibliographyKeys,
73 #[serde(rename = "format")]
74 Format,
75 #[serde(rename = "jscmd")]
76 JavascriptCommand,
77}