open_library/models/
authors.rs

1use crate::format::KeyedValue;
2use crate::models::identifiers::OpenLibraryIdentifier;
3use crate::models::works::Work;
4use crate::models::{Link, LinkName, OpenLibraryModel, OpenLibraryResource};
5use crate::OpenLibraryError;
6use chrono::NaiveDateTime;
7use serde::de::Error;
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9use std::borrow::Cow;
10use std::collections::HashMap;
11use std::convert::TryFrom;
12use std::fmt;
13use std::fmt::Display;
14use std::str::FromStr;
15use url::Url;
16
17#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
18pub struct Author {
19    pub key: String,
20    #[serde(default)]
21    #[serde(skip_serializing_if = "Vec::is_empty")]
22    pub text: Vec<String>,
23    #[serde(rename(deserialize = "type"))]
24    pub r#type: String,
25    pub name: String,
26    #[serde(default)]
27    #[serde(skip_serializing_if = "Vec::is_empty")]
28    pub alternate_names: Vec<String>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub birth_date: Option<String>,
31    pub top_work: String,
32    pub work_count: i32,
33    pub top_subjects: Vec<String>,
34    pub _version_: u64,
35}
36
37#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
38pub struct AuthorReference {
39    #[serde(rename = "type")]
40    #[serde(deserialize_with = "deserialize_author_type")]
41    pub author_type: KeyedValue<AuthorType>,
42    #[serde(rename = "author")]
43    pub identifier: KeyedValue<OpenLibraryResource>,
44}
45
46#[derive(Debug, Eq, PartialEq)]
47pub enum AuthorType {
48    AuthorRole,
49}
50
51impl Display for AuthorType {
52    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53        match self {
54            AuthorType::AuthorRole => write!(f, "/type/author_role"),
55        }
56    }
57}
58
59impl FromStr for AuthorType {
60    type Err = OpenLibraryError;
61
62    fn from_str(value: &str) -> Result<Self, Self::Err> {
63        match value {
64            "author_role" => Ok(Self::AuthorRole),
65            _ => Err(OpenLibraryError::ParsingError {
66                reason: format!("Unable to parse string ({}) into an Author Type", &value),
67            }),
68        }
69    }
70}
71
72impl<'de> Deserialize<'de> for AuthorType {
73    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
74    where
75        D: Deserializer<'de>,
76    {
77        let value: String = Deserialize::deserialize(deserializer).map_err(D::Error::custom)?;
78
79        let chunks = value
80            .split('/')
81            .filter(|str| !str.is_empty())
82            .collect::<Vec<&str>>();
83
84        match chunks.get(0) {
85            Some(&"type") => match chunks.get(1) {
86                Some(value) => Ok(AuthorType::from_str(*value).map_err(D::Error::custom)?),
87                None => Err(D::Error::custom("No Author Type was provided!")),
88            },
89            _ => Err(D::Error::custom(format!(
90                "Invalid format for Author Type: {}",
91                &value
92            ))),
93        }
94    }
95}
96
97impl Serialize for AuthorType {
98    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
99    where
100        S: Serializer,
101    {
102        serializer.serialize_str(self.to_string().as_str())
103    }
104}
105
106#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
107pub struct AuthorDetails {
108    #[serde(skip_serializing_if = "Option::is_none")]
109    pub title: Option<String>,
110    #[serde(default)]
111    #[serde(skip_serializing_if = "Vec::is_empty")]
112    pub source_records: Vec<String>, //TODO parse records
113    pub key: OpenLibraryResource,
114    #[serde(default)]
115    #[serde(with = "crate::format::value")]
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub bio: Option<String>,
118    pub photos: Vec<i32>,
119    // #[serde(with = "crate::format::date")]
120    pub birth_date: String,
121    pub personal_name: String,
122    pub remote_ids: HashMap<String, String>,
123    pub entity_type: Option<String>, //TODO: should be enum
124    pub links: Vec<Link>,
125    pub name: String,
126    pub alternate_names: Vec<String>,
127    pub wikipedia: Option<Url>,
128    pub latest_revision: u16,
129    pub revision: u16,
130    #[serde(with = "crate::format::value")]
131    pub created: Option<NaiveDateTime>,
132    #[serde(with = "crate::format::value")]
133    pub last_modified: Option<NaiveDateTime>,
134}
135
136impl OpenLibraryModel for AuthorDetails {}
137
138#[derive(Deserialize, Debug, Eq, PartialEq, Serialize)]
139pub struct AuthorWorksRequest {
140    pub identifier: OpenLibraryIdentifier,
141    pub limit: Option<u32>,
142    pub offset: Option<u32>,
143}
144
145impl TryFrom<OpenLibraryIdentifier> for AuthorWorksRequest {
146    type Error = OpenLibraryError;
147
148    fn try_from(identifier: OpenLibraryIdentifier) -> Result<Self, OpenLibraryError> {
149        Ok(Self {
150            identifier,
151            limit: None,
152            offset: None,
153        })
154    }
155}
156
157impl TryFrom<Url> for AuthorWorksRequest {
158    type Error = OpenLibraryError;
159
160    fn try_from(value: Url) -> Result<Self, Self::Error> {
161        let path_segments = value
162            .path_segments()
163            .ok_or(OpenLibraryError::ParsingError {
164                reason: "Invalid URL supplied, no path segments found".to_string(),
165            })?
166            .collect::<Vec<&str>>();
167
168        let path_index = path_segments.iter().position(|x| *x == "authors").ok_or(
169            OpenLibraryError::ParsingError {
170                reason: "Invalid URL supplied, unable to determine author identifier".to_string(),
171            },
172        )?;
173
174        let query_parameters = value
175            .query_pairs()
176            .collect::<HashMap<Cow<'_, str>, Cow<'_, str>>>();
177
178        let result = *path_segments
179            .get(path_index + 1)
180            .ok_or(OpenLibraryError::ParsingError {
181                reason: "Unable to find an author identifier within the URL path".to_string(),
182            })?;
183
184        let limit = match query_parameters.get("limit") {
185            Some(x) => Some(x.clone().into_owned().parse::<u32>().map_err(|e| {
186                OpenLibraryError::ParsingError {
187                    reason: e.to_string(),
188                }
189            })?),
190            None => None,
191        };
192
193        let offset = match query_parameters.get("offset") {
194            Some(z) => Some(z.clone().into_owned().parse::<u32>().map_err(|e| {
195                OpenLibraryError::ParsingError {
196                    reason: e.to_string(),
197                }
198            })?),
199            None => None,
200        };
201
202        Ok(Self {
203            identifier: OpenLibraryIdentifier::from_str(result)?,
204            limit: limit,
205            offset: offset,
206        })
207    }
208}
209
210#[derive(Deserialize, Debug, Eq, PartialEq, Serialize)]
211pub struct AuthorWorksResponse {
212    #[serde(default)]
213    #[serde(skip_serializing_if = "HashMap::is_empty")]
214    pub links: HashMap<LinkName, String>,
215    pub size: u32,
216    #[serde(default)]
217    #[serde(skip_serializing_if = "Vec::is_empty")]
218    pub entries: Vec<Work>,
219}
220
221impl OpenLibraryModel for AuthorWorksResponse {}
222
223#[derive(Debug, Deserialize, Serialize)]
224pub struct AuthorResponse {
225    #[serde(rename = "numFound")]
226    pub num_found: i32,
227    pub start: i32,
228    #[serde(rename = "numFoundExact")]
229    pub num_found_exact: bool,
230    pub docs: Vec<Author>,
231}
232
233impl OpenLibraryModel for AuthorResponse {}
234
235fn deserialize_author_type<'de, D>(deserializer: D) -> Result<KeyedValue<AuthorType>, D::Error>
236where
237    D: Deserializer<'de>,
238{
239    #[derive(Deserialize)]
240    #[serde(untagged)]
241    enum StringOrKeyedValue {
242        String(AuthorType),
243        KeyedValue(KeyedValue<AuthorType>),
244    }
245
246    Ok(match StringOrKeyedValue::deserialize(deserializer)? {
247        StringOrKeyedValue::String(v) => KeyedValue { key: v },
248        StringOrKeyedValue::KeyedValue(v) => v,
249    })
250}