rs_plugin_common_interfaces/domain/
book.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::domain::{other_ids::OtherIds, person::Person, 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 pub people: Option<Vec<Person>>,
93}
94
95impl BookForUpdate {
96 pub fn has_update(&self) -> bool {
97 self != &BookForUpdate::default()
98 }
99}
100
101
102#[cfg(test)]
103mod tests {
104 use super::Book;
105 use crate::domain::other_ids::OtherIds;
106 use serde_json::json;
107
108 #[test]
109 fn book_otherids_serializes_as_array_and_rejects_string() {
110 let book = Book {
111 id: "book-1".to_string(),
112 name: "Book 1".to_string(),
113 otherids: Some(OtherIds(vec!["goodreads:321".to_string()])),
114 ..Default::default()
115 };
116 let value = serde_json::to_value(&book).unwrap();
117 assert_eq!(value.get("otherids"), Some(&json!(["goodreads:321"])));
118
119 let parsed: Book = serde_json::from_value(json!({
120 "id": "book-1",
121 "name": "Book 1",
122 "otherids": ["custom:1"]
123 }))
124 .unwrap();
125 assert_eq!(
126 parsed.otherids,
127 Some(OtherIds(vec!["custom:1".to_string()]))
128 );
129
130 let invalid = serde_json::from_value::<Book>(json!({
131 "id": "book-1",
132 "name": "Book 1",
133 "otherids": "custom:1"
134 }));
135 assert!(invalid.is_err());
136 }
137}