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