Skip to main content

rs_plugin_common_interfaces/domain/
book.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::domain::{other_ids::OtherIds, rs_ids::RsIds};
5
6#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
7#[serde(rename_all = "camelCase")]
8pub struct Book {
9    #[serde(default)]
10    pub id: String,
11    pub name: String,
12    #[serde(skip_serializing_if = "Option::is_none")]
13    #[serde(rename = "type")]
14    pub kind: Option<String>,
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub serie_ref: Option<String>,
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub volume: Option<f64>,
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub chapter: Option<f64>,
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub year: Option<u16>,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub airdate: Option<i64>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub overview: Option<String>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub pages: Option<u32>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub params: Option<Value>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub lang: Option<String>,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub original: Option<String>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub isbn13: Option<String>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub openlibrary_edition_id: Option<String>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub openlibrary_work_id: Option<String>,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub google_books_volume_id: Option<String>,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub asin: Option<String>,
45    pub otherids: Option<OtherIds>,
46    #[serde(default)]
47    pub modified: u64,
48    #[serde(default)]
49    pub added: u64,
50}
51
52impl From<Book> for RsIds {
53    fn from(value: Book) -> Self {
54        RsIds {
55            redseat: Some(value.id),
56            isbn13: value.isbn13,
57            openlibrary_edition_id: value.openlibrary_edition_id,
58            openlibrary_work_id: value.openlibrary_work_id,
59            google_books_volume_id: value.google_books_volume_id,
60            asin: value.asin,
61            other_ids: value.otherids,
62            volume: value.volume,
63            chapter: value.chapter,
64            ..Default::default()
65        }
66    }
67}
68
69#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
70#[serde(rename_all = "camelCase")]
71pub struct BookForUpdate {
72    pub name: Option<String>,
73    #[serde(rename = "type")]
74    pub kind: Option<String>,
75    pub serie_ref: Option<String>,
76    pub volume: Option<f64>,
77    pub chapter: Option<f64>,
78    pub year: Option<u16>,
79    pub airdate: Option<i64>,
80    pub overview: Option<String>,
81    pub pages: Option<u32>,
82    pub params: Option<Value>,
83    pub lang: Option<String>,
84    pub original: Option<String>,
85    pub isbn13: Option<String>,
86    pub openlibrary_edition_id: Option<String>,
87    pub openlibrary_work_id: Option<String>,
88    pub google_books_volume_id: Option<String>,
89    pub asin: Option<String>,
90    pub otherids: Option<OtherIds>,
91}
92
93impl BookForUpdate {
94    pub fn has_update(&self) -> bool {
95        self != &BookForUpdate::default()
96    }
97}
98
99
100#[cfg(test)]
101mod tests {
102    use super::Book;
103    use crate::domain::other_ids::OtherIds;
104    use serde_json::json;
105
106    #[test]
107    fn book_otherids_serializes_as_array_and_rejects_string() {
108        let book = Book {
109            id: "book-1".to_string(),
110            name: "Book 1".to_string(),
111            otherids: Some(OtherIds(vec!["goodreads:321".to_string()])),
112            ..Default::default()
113        };
114        let value = serde_json::to_value(&book).unwrap();
115        assert_eq!(value.get("otherids"), Some(&json!(["goodreads:321"])));
116
117        let parsed: Book = serde_json::from_value(json!({
118            "id": "book-1",
119            "name": "Book 1",
120            "otherids": ["custom:1"]
121        }))
122        .unwrap();
123        assert_eq!(
124            parsed.otherids,
125            Some(OtherIds(vec!["custom:1".to_string()]))
126        );
127
128        let invalid = serde_json::from_value::<Book>(json!({
129            "id": "book-1",
130            "name": "Book 1",
131            "otherids": "custom:1"
132        }));
133        assert!(invalid.is_err());
134    }
135}