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#[cfg(test)]
70mod tests {
71    use super::Book;
72    use crate::domain::other_ids::OtherIds;
73    use serde_json::json;
74
75    #[test]
76    fn book_otherids_serializes_as_array_and_rejects_string() {
77        let book = Book {
78            id: "book-1".to_string(),
79            name: "Book 1".to_string(),
80            otherids: Some(OtherIds(vec!["goodreads:321".to_string()])),
81            ..Default::default()
82        };
83        let value = serde_json::to_value(&book).unwrap();
84        assert_eq!(value.get("otherids"), Some(&json!(["goodreads:321"])));
85
86        let parsed: Book = serde_json::from_value(json!({
87            "id": "book-1",
88            "name": "Book 1",
89            "otherids": ["custom:1"]
90        }))
91        .unwrap();
92        assert_eq!(
93            parsed.otherids,
94            Some(OtherIds(vec!["custom:1".to_string()]))
95        );
96
97        let invalid = serde_json::from_value::<Book>(json!({
98            "id": "book-1",
99            "name": "Book 1",
100            "otherids": "custom:1"
101        }));
102        assert!(invalid.is_err());
103    }
104}