open_library/models/
books.rs

1use crate::models::{Link, OpenLibraryModel, OpenLibraryResource};
2use crate::OpenLibraryError;
3use serde::de::Error;
4use serde::{Deserialize, Deserializer, Serialize};
5use std::collections::HashMap;
6use std::fmt::{Display, Formatter};
7use std::str::FromStr;
8use url::Url;
9
10#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
11pub struct Book {
12    #[serde(default)]
13    #[serde(deserialize_with = "url_or_list")]
14    #[serde(skip_serializing_if = "Vec::is_empty")]
15    pub url: Vec<Url>,
16    pub key: OpenLibraryResource,
17    pub title: String,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub subtitle: Option<String>,
20    pub pagination: Option<String>,
21    pub by_statement: Option<String>,
22    pub notes: Option<String>,
23    #[serde(default)]
24    #[serde(skip_serializing_if = "Vec::is_empty")]
25    pub authors: Vec<Author>,
26    #[serde(default)]
27    pub identifiers: HashMap<BookIdentifierKey, Vec<String>>,
28    #[serde(default)]
29    #[serde(skip_serializing_if = "Classifications::is_default")]
30    pub classifications: Classifications,
31    #[serde(default)]
32    #[serde(deserialize_with = "strings_or_entities")]
33    #[serde(skip_serializing_if = "Vec::is_empty")]
34    pub subjects: Vec<String>,
35    #[serde(default)]
36    #[serde(skip_serializing_if = "Vec::is_empty")]
37    pub subject_places: Vec<Entity>,
38    #[serde(default)]
39    #[serde(skip_serializing_if = "Vec::is_empty")]
40    pub subject_people: Vec<Entity>,
41    #[serde(default)]
42    #[serde(skip_serializing_if = "Vec::is_empty")]
43    pub subject_times: Vec<Entity>,
44    #[serde(default)]
45    #[serde(deserialize_with = "strings_or_entities")]
46    #[serde(skip_serializing_if = "Vec::is_empty")]
47    pub publishers: Vec<String>,
48    #[serde(default)]
49    #[serde(deserialize_with = "strings_or_entities")]
50    #[serde(skip_serializing_if = "Vec::is_empty")]
51    pub publish_places: Vec<String>,
52    pub publish_date: String,
53    #[serde(default)]
54    #[serde(skip_serializing_if = "Vec::is_empty")]
55    pub excerpts: Vec<Excerpt>,
56    #[serde(default)]
57    #[serde(skip_serializing_if = "Vec::is_empty")]
58    pub links: Vec<Link>,
59    #[serde(default)]
60    #[serde(rename = "covers")]
61    pub cover_images: Vec<u32>,
62    #[serde(default)]
63    #[serde(skip_serializing_if = "Vec::is_empty")]
64    pub ebooks: Vec<ElectronicBook>,
65    pub number_of_pages: Option<u32>,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub weight: Option<String>,
68}
69
70impl OpenLibraryModel for Book {}
71
72#[derive(Clone, Debug, Eq, Hash, PartialEq)]
73pub enum BibliographyKey {
74    ISBN(String),
75    LCCN(String),
76    OCLC(String),
77    OLID(String),
78}
79
80impl BibliographyKey {
81    pub fn from_tuple((key, value): (String, String)) -> Result<Self, OpenLibraryError> {
82        match key.as_str() {
83            "ISBN" => Ok(BibliographyKey::ISBN(value)),
84            "LCCN" => Ok(BibliographyKey::LCCN(value)),
85            "OCLC" => Ok(BibliographyKey::OCLC(value)),
86            "OLID" => Ok(BibliographyKey::OLID(value)),
87            _ => Err(OpenLibraryError::ParsingError {
88                reason: format!(
89                    "Unable to determine bibliography key from specific value ({})",
90                    value
91                ),
92            }),
93        }
94    }
95}
96
97impl Display for BibliographyKey {
98    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
99        match self {
100            BibliographyKey::ISBN(value) => write!(f, "ISBN:{}", value)?,
101            BibliographyKey::LCCN(value) => write!(f, "LCCN:{}", value)?,
102            BibliographyKey::OCLC(value) => write!(f, "OCLC:{}", value)?,
103            BibliographyKey::OLID(value) => write!(f, "OLID:{}", value)?,
104        }
105        Ok(())
106    }
107}
108
109impl<'de> serde::Deserialize<'de> for BibliographyKey {
110    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
111    where
112        D: Deserializer<'de>,
113    {
114        let value: String =
115            serde::Deserialize::deserialize(deserializer).map_err(D::Error::custom)?;
116        let chunks: Vec<&str> = value.split(':').collect();
117
118        if chunks.len() != 2 {
119            return Err(D::Error::custom("The specified value {} has improper form"));
120        }
121
122        let key = match chunks.get(0) {
123            Some(value) => Ok(*value),
124            None => Err(D::Error::custom(format!(
125                "Supplied identifier string has improper format {}",
126                &value
127            ))),
128        }?
129        .to_string();
130
131        let value = match chunks.get(1) {
132            Some(string) => Ok(*string),
133            None => Err(D::Error::custom(format!(
134                "Supplied identifier string has improper format {}",
135                &value
136            ))),
137        }?
138        .to_string();
139
140        BibliographyKey::from_tuple((key, value)).map_err(D::Error::custom)
141    }
142}
143
144impl serde::Serialize for BibliographyKey {
145    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
146    where
147        S: serde::Serializer,
148    {
149        serializer.serialize_str(format!("{}", self).as_str())
150    }
151}
152
153#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
154pub enum BookIdentifierKey {
155    #[serde(alias = "isbn_10")]
156    InternationalStandard10, // International Standard Book Number - 10 Digits
157    #[serde(alias = "isbn_13")]
158    InternationalStandard13, // International Standard Book Number - 13 Digits
159    #[serde(alias = "lccn")]
160    LibraryOfCongress, // Library of Congress Control Number
161    #[serde(alias = "oclc")]
162    OhioCollegeLibraryCenter, // Ohio College Library Center https://en.wikipedia.org/wiki/OCLC
163    #[serde(alias = "goodreads")]
164    GoodReads,
165    #[serde(alias = "openlibrary")]
166    OpenLibrary,
167    #[serde(alias = "librarything")]
168    LibraryThing,
169    #[serde(alias = "project_gutenberg")]
170    ProjectGutenberg,
171    #[serde(alias = "wikidata")]
172    WikiData,
173}
174
175impl BookIdentifierKey {
176    pub fn from_isbn(value: &str) -> Result<Self, OpenLibraryError> {
177        match value.len() {
178            10 => Ok(BookIdentifierKey::InternationalStandard10),
179            13 => Ok(BookIdentifierKey::InternationalStandard13),
180            _ => Err(OpenLibraryError::ParsingError {
181                reason: format!("Invalid length ({}) for ISBN", value.len()),
182            }),
183        }
184    }
185}
186
187impl Display for BookIdentifierKey {
188    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
189        match self {
190            BookIdentifierKey::InternationalStandard10 => Ok(write!(f, "isbn_10")?),
191            BookIdentifierKey::InternationalStandard13 => Ok(write!(f, "isbn_13")?),
192            BookIdentifierKey::LibraryOfCongress => Ok(write!(f, "lccn")?),
193            BookIdentifierKey::OhioCollegeLibraryCenter => Ok(write!(f, "oclc")?),
194            BookIdentifierKey::GoodReads => Ok(write!(f, "goodreads")?),
195            BookIdentifierKey::OpenLibrary => Ok(write!(f, "openlibrary")?),
196            BookIdentifierKey::LibraryThing => Ok(write!(f, "librarything")?),
197            BookIdentifierKey::ProjectGutenberg => Ok(write!(f, "project_gutenberg")?),
198            BookIdentifierKey::WikiData => Ok(write!(f, "wikidata")?),
199        }
200    }
201}
202
203impl FromStr for BookIdentifierKey {
204    type Err = OpenLibraryError;
205
206    fn from_str(s: &str) -> Result<Self, Self::Err> {
207        match s {
208            "isbn_10" => Ok(BookIdentifierKey::InternationalStandard10),
209            "isbn_13" => Ok(BookIdentifierKey::InternationalStandard13),
210            "lccn" => Ok(BookIdentifierKey::LibraryOfCongress),
211            "oclc" => Ok(BookIdentifierKey::OhioCollegeLibraryCenter),
212            "goodreads" => Ok(BookIdentifierKey::GoodReads),
213            "openlibrary" => Ok(BookIdentifierKey::OpenLibrary),
214            "librarything" => Ok(BookIdentifierKey::LibraryThing),
215            "project_gutenberg" => Ok(BookIdentifierKey::ProjectGutenberg),
216            "wikidata" => Ok(BookIdentifierKey::WikiData),
217            _ => Err(OpenLibraryError::ParsingError {
218                reason: format!(
219                    "Unable to parse supplied value ({}) into a book identifier!",
220                    s
221                ),
222            }),
223        }
224    }
225}
226
227//TODO: this is a duplicate struct name
228// and also wrong
229#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
230pub struct Author {
231    #[serde(skip_serializing_if = "Option::is_none")]
232    pub key: Option<OpenLibraryResource>,
233    #[serde(skip_serializing_if = "Option::is_none")]
234    pub name: Option<String>,
235    #[serde(skip_serializing_if = "Option::is_none")]
236    pub url: Option<String>,
237}
238
239#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
240pub struct Entity {
241    #[serde(skip_serializing_if = "String::is_empty")]
242    pub name: String,
243    #[serde(skip_serializing_if = "Option::is_none")]
244    pub url: Option<String>,
245}
246
247#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
248pub struct Classifications {
249    #[serde(default)]
250    #[serde(rename(deserialize = "dewey_decimal_class"))]
251    pub dewey_decimal: Vec<String>,
252    #[serde(default)]
253    #[serde(rename(deserialize = "lc_classifications"))]
254    pub library_of_congress: Vec<String>,
255}
256
257impl Classifications {
258    pub fn is_default(&self) -> bool {
259        self.dewey_decimal.is_empty() && self.library_of_congress.is_empty()
260    }
261}
262
263#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
264pub struct ElectronicBook {
265    preview_url: String,
266    availability: String, //Should be enum but don't know possible values ("restricted",...)
267                          // formats: ?  //Don't know the form of the struct yet
268}
269
270#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
271pub struct Excerpt {
272    comment: String,
273    text: String,
274}
275
276// Necessary since the `publishers` field can either be Vec<String> or Vec<Entity> based on the endpoint
277// Book Search:
278//    "publishers": [
279//       {
280//         "name": "Anchor Books"
281//       }
282//     ]
283// By ISBN:
284//    "publishers": [
285//      "Addison-Wesley"
286//     ]
287fn strings_or_entities<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
288where
289    D: Deserializer<'de>,
290{
291    #[derive(Deserialize)]
292    #[serde(untagged)]
293    enum StrOrEntity {
294        Str(Vec<String>),
295        Entity(Vec<Entity>),
296    }
297
298    Ok(match StrOrEntity::deserialize(deserializer)? {
299        StrOrEntity::Str(v) => v,
300        StrOrEntity::Entity(v) => v.into_iter().map(|x| x.name).collect(),
301    })
302}
303
304// Necessary since the `url` field can either be a String or Vec<Url> based on the endpoint
305// Book Search:
306//    "publishers": [
307//       {
308//         "name": "Anchor Books"
309//       }
310//     ]
311// By ISBN:
312//    "publishers": [
313//      "Addison-Wesley"
314//     ]
315fn url_or_list<'de, D>(deserializer: D) -> Result<Vec<Url>, D::Error>
316where
317    D: Deserializer<'de>,
318{
319    #[derive(Deserialize)]
320    #[serde(untagged)]
321    enum UrlOrVector {
322        Url(Url),
323        Vector(Vec<Url>),
324    }
325
326    Ok(match UrlOrVector::deserialize(deserializer)? {
327        UrlOrVector::Url(value) => vec![value],
328        UrlOrVector::Vector(values) => values,
329    })
330}