rs_plugin_common_interfaces/domain/
book.rs1use 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}