rs_plugin_common_interfaces/domain/
book.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::domain::{
5 other_ids::OtherIds,
6 person::Person,
7 rs_ids::{ApplyRsIds, RsIds},
8 tag::Tag,
9};
10
11#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
12#[serde(rename_all = "camelCase")]
13pub struct Book {
14 #[serde(default)]
15 pub id: String,
16 pub name: String,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 #[serde(rename = "type")]
19 pub kind: Option<String>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub serie_ref: Option<String>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub volume: Option<f64>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub chapter: Option<f64>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub year: Option<u16>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub airdate: Option<i64>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub overview: Option<String>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub pages: Option<u32>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub params: Option<Value>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub lang: Option<String>,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub original: Option<String>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub isbn13: Option<String>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub openlibrary_edition_id: Option<String>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub openlibrary_work_id: Option<String>,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub google_books_volume_id: Option<String>,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub asin: Option<String>,
50 pub otherids: Option<OtherIds>,
51 #[serde(default)]
52 pub modified: u64,
53 #[serde(default)]
54 pub added: u64,
55}
56
57impl From<Book> for RsIds {
58 fn from(value: Book) -> Self {
59 RsIds {
60 redseat: Some(value.id),
61 isbn13: value.isbn13,
62 openlibrary_edition_id: value.openlibrary_edition_id,
63 openlibrary_work_id: value.openlibrary_work_id,
64 google_books_volume_id: value.google_books_volume_id,
65 asin: value.asin,
66 other_ids: value.otherids,
67 volume: value.volume,
68 chapter: value.chapter,
69 ..Default::default()
70 }
71 }
72}
73
74impl ApplyRsIds for Book {
75 fn apply_rs_ids(&mut self, ids: &RsIds) {
76 if let Some(redseat) = ids.redseat.as_ref() {
77 self.id = redseat.clone();
78 }
79 if let Some(isbn13) = ids.isbn13.as_ref() {
80 self.isbn13 = Some(isbn13.clone());
81 }
82 if let Some(openlibrary_edition_id) = ids.openlibrary_edition_id.as_ref() {
83 self.openlibrary_edition_id = Some(openlibrary_edition_id.clone());
84 }
85 if let Some(openlibrary_work_id) = ids.openlibrary_work_id.as_ref() {
86 self.openlibrary_work_id = Some(openlibrary_work_id.clone());
87 }
88 if let Some(google_books_volume_id) = ids.google_books_volume_id.as_ref() {
89 self.google_books_volume_id = Some(google_books_volume_id.clone());
90 }
91 if let Some(asin) = ids.asin.as_ref() {
92 self.asin = Some(asin.clone());
93 }
94 if let Some(other_ids) = ids.other_ids.as_ref() {
95 self.otherids = Some(other_ids.clone());
96 }
97 if let Some(volume) = ids.volume {
98 self.volume = Some(volume);
99 }
100 if let Some(chapter) = ids.chapter {
101 self.chapter = Some(chapter);
102 }
103 }
104}
105
106#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
107#[serde(rename_all = "camelCase")]
108pub struct BookForUpdate {
109 pub name: Option<String>,
110 #[serde(rename = "type")]
111 pub kind: Option<String>,
112 pub serie_ref: Option<String>,
113 pub volume: Option<f64>,
114 pub chapter: Option<f64>,
115 pub year: Option<u16>,
116 pub airdate: Option<i64>,
117 pub overview: Option<String>,
118 pub pages: Option<u32>,
119 pub params: Option<Value>,
120 pub lang: Option<String>,
121 pub original: Option<String>,
122 pub isbn13: Option<String>,
123 pub openlibrary_edition_id: Option<String>,
124 pub openlibrary_work_id: Option<String>,
125 pub google_books_volume_id: Option<String>,
126 pub asin: Option<String>,
127 pub otherids: Option<OtherIds>,
128
129 pub people: Option<Vec<Person>>,
130 pub tags: Option<Vec<Tag>>,
131}
132
133impl BookForUpdate {
134 pub fn has_update(&self) -> bool {
135 self != &BookForUpdate::default()
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::Book;
142 use crate::domain::{
143 other_ids::OtherIds,
144 rs_ids::{ApplyRsIds, RsIds},
145 };
146 use serde_json::json;
147
148 #[test]
149 fn book_otherids_serializes_as_array_and_rejects_string() {
150 let book = Book {
151 id: "book-1".to_string(),
152 name: "Book 1".to_string(),
153 otherids: Some(OtherIds(vec!["goodreads:321".to_string()])),
154 ..Default::default()
155 };
156 let value = serde_json::to_value(&book).unwrap();
157 assert_eq!(value.get("otherids"), Some(&json!(["goodreads:321"])));
158
159 let parsed: Book = serde_json::from_value(json!({
160 "id": "book-1",
161 "name": "Book 1",
162 "otherids": ["custom:1"]
163 }))
164 .unwrap();
165 assert_eq!(
166 parsed.otherids,
167 Some(OtherIds(vec!["custom:1".to_string()]))
168 );
169
170 let invalid = serde_json::from_value::<Book>(json!({
171 "id": "book-1",
172 "name": "Book 1",
173 "otherids": "custom:1"
174 }));
175 assert!(invalid.is_err());
176 }
177
178 #[test]
179 fn book_apply_rs_ids_updates_only_present_values() {
180 let mut book = Book {
181 id: "book-old".to_string(),
182 name: "Book 1".to_string(),
183 openlibrary_work_id: Some("olw-old".to_string()),
184 chapter: Some(7.0),
185 ..Default::default()
186 };
187 let ids = RsIds {
188 redseat: Some("book-new".to_string()),
189 isbn13: Some("9783161484100".to_string()),
190 openlibrary_edition_id: Some("ole-new".to_string()),
191 google_books_volume_id: Some("gb-1".to_string()),
192 asin: Some("B00TEST".to_string()),
193 other_ids: Some(OtherIds(vec!["goodreads:42".to_string()])),
194 volume: Some(3.0),
195 ..Default::default()
196 };
197
198 book.apply_rs_ids(&ids);
199
200 assert_eq!(book.id, "book-new");
201 assert_eq!(book.isbn13.as_deref(), Some("9783161484100"));
202 assert_eq!(book.openlibrary_edition_id.as_deref(), Some("ole-new"));
203 assert_eq!(book.openlibrary_work_id.as_deref(), Some("olw-old"));
204 assert_eq!(book.google_books_volume_id.as_deref(), Some("gb-1"));
205 assert_eq!(book.asin.as_deref(), Some("B00TEST"));
206 assert_eq!(
207 book.otherids,
208 Some(OtherIds(vec!["goodreads:42".to_string()]))
209 );
210 assert_eq!(book.volume, Some(3.0));
211 assert_eq!(book.chapter, Some(7.0));
212 }
213}