1use chrono::NaiveDate;
2use language_tags::LanguageTag;
3use serde::{Deserialize, Serialize};
4use std::collections::{HashMap, HashSet};
5use url::Url;
6use uuid::Uuid;
7
8#[cfg(feature = "fb2")]
9mod fb2;
10
11#[derive(Debug, PartialEq, Deserialize, Serialize)]
12pub struct Book {
13 pub id: Uuid,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub language: Option<LanguageTag>,
16 pub short_title: String,
17 pub date: Date,
18 pub authors: Vec<Author>,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub cover: Option<InlineImage>,
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub annotation: Option<Annotation>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub title: Option<Title>,
25 #[serde(default, skip_serializing_if = "Vec::is_empty")]
26 pub epigraphs: Vec<Epigraph>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub notes: Option<Footnotes>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub comments: Option<Footnotes>,
31 pub chapters: Vec<Chapter>,
32}
33
34#[derive(Debug, Default, PartialEq, Deserialize, Serialize)]
35pub struct Date {
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub iso_date: Option<NaiveDate>,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub display_date: Option<String>,
40}
41
42#[derive(Debug, PartialEq, Deserialize, Serialize)]
43pub struct Author {
44 pub id: Uuid,
45 pub full_name: String,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub given_name: Option<String>,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub family_name: Option<String>,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub middle_name: Option<String>,
52}
53
54#[derive(Debug, PartialEq, Deserialize, Serialize)]
55pub struct Footnotes {
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub title: Option<Title>,
58 pub content: HashMap<String, Footnote>,
59}
60
61#[derive(Debug, PartialEq, Deserialize, Serialize)]
62pub struct Footnote {
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub title: Option<Title>,
65 pub content: Vec<Content>,
66}
67
68#[derive(Debug, PartialEq, Deserialize, Serialize)]
69pub struct Chapter {
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub anchor: Option<String>,
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub title: Option<Title>,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub annotation: Option<Annotation>,
76 #[serde(skip_serializing_if = "Option::is_none")]
77 pub cover: Option<Image>,
78 #[serde(default, skip_serializing_if = "Vec::is_empty")]
79 pub epigraphs: Vec<Epigraph>,
80 pub content: Vec<Content>,
81 pub sub_chapters: Vec<Chapter>,
82}
83
84#[derive(Debug, PartialEq, Deserialize, Serialize)]
85pub enum Content {
86 Paragraph(Paragraph),
87 Poem(Poem),
88 Subtitle(Paragraph),
89 Cite(Cite),
90 Table(Table),
91 Image(Image),
92 EmptyLine,
93}
94
95#[derive(Debug, PartialEq, Deserialize, Serialize)]
96pub struct Annotation {
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub anchor: Option<String>,
99 pub content: Vec<AnnotationElement>,
100}
101
102#[derive(Debug, PartialEq, Deserialize, Serialize)]
103pub enum AnnotationElement {
104 Paragraph(Paragraph),
105 Poem(Poem),
106 Cite(Cite),
107 Subtitle(Paragraph),
108 Table(Table),
109 EmptyLine,
110}
111
112#[derive(Debug, PartialEq, Deserialize, Serialize)]
113pub struct Epigraph {
114 #[serde(skip_serializing_if = "Option::is_none")]
115 pub anchor: Option<String>,
116 #[serde(default, skip_serializing_if = "Vec::is_empty")]
117 pub authors: Vec<Paragraph>,
118 pub content: Vec<EpigraphElement>,
119}
120
121#[derive(Debug, PartialEq, Deserialize, Serialize)]
122pub enum EpigraphElement {
123 Paragraph(Paragraph),
124 Poem(Poem),
125 Cite(Cite),
126 EmptyLine,
127}
128
129#[derive(Debug, PartialEq, Deserialize, Serialize)]
130pub struct Poem {
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub anchor: Option<String>,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub title: Option<Title>,
135 #[serde(default, skip_serializing_if = "Vec::is_empty")]
136 pub epigraphs: Vec<Epigraph>,
137 #[serde(default, skip_serializing_if = "Vec::is_empty")]
139 pub authors: Vec<Paragraph>,
140 pub content: Vec<PoemElement>,
141}
142
143#[derive(Debug, PartialEq, Deserialize, Serialize)]
144pub enum PoemElement {
145 Subtitle(Paragraph),
146 Stanza(Stanza),
147}
148
149#[derive(Debug, PartialEq, Deserialize, Serialize)]
150pub struct Stanza {
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub title: Option<Title>,
153 #[serde(skip_serializing_if = "Option::is_none")]
155 pub subtitle: Option<Paragraph>,
156 pub content: Vec<Paragraph>,
158}
159
160#[derive(Debug, PartialEq, Deserialize, Serialize)]
161pub struct Cite {
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub anchor: Option<String>,
164 #[serde(default, skip_serializing_if = "Vec::is_empty")]
166 pub authors: Vec<Paragraph>,
167 pub content: Vec<CiteElement>,
168}
169
170#[derive(Debug, PartialEq, Deserialize, Serialize)]
171pub enum CiteElement {
172 Paragraph(Paragraph),
173 Poem(Poem),
174 Subtitle(Paragraph),
175 Table(Table),
176 EmptyLine,
177}
178
179#[derive(Debug, PartialEq, Deserialize, Serialize)]
180pub struct Title {
181 pub content: Vec<TitleElement>,
182}
183
184#[derive(Debug, PartialEq, Deserialize, Serialize)]
186pub enum TitleElement {
187 Paragraph(Paragraph),
188 EmptyLine,
189}
190
191#[derive(Debug, PartialEq, Deserialize, Serialize)]
192pub struct Paragraph {
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub anchor: Option<String>,
195 pub content: Vec<Span>,
196}
197
198#[derive(Debug, PartialEq, Deserialize, Serialize)]
199pub struct Table {
200 #[serde(skip_serializing_if = "Option::is_none")]
201 pub anchor: Option<String>,
202 pub header_column: bool,
203 pub header_row: bool,
204 pub rows: Vec<TableRow>,
205}
206
207#[derive(Debug, PartialEq, Deserialize, Serialize)]
208pub struct TableRow {
209 pub cells: Vec<TableCell>,
210}
211
212#[derive(Debug, PartialEq, Deserialize, Serialize)]
213pub struct TableCell {
214 #[serde(skip_serializing_if = "Option::is_none")]
215 pub anchor: Option<String>,
216 pub content: Vec<Span>,
217}
218
219#[derive(Debug, PartialEq, Deserialize, Serialize)]
220pub struct Image {
221 pub id: Uuid,
222 #[serde(skip_serializing_if = "Option::is_none")]
223 pub anchor: Option<String>,
224 #[serde(skip_serializing_if = "Option::is_none")]
225 pub alt: Option<String>,
226 #[serde(skip_serializing_if = "Option::is_none")]
227 pub title: Option<String>,
228}
229
230#[derive(Debug, PartialEq, Deserialize, Serialize)]
231pub enum Span {
232 Footnote(FootnoteLink),
233 Link(Link),
234 Image(InlineImage),
235 Text(Text),
236}
237
238#[derive(Debug, PartialEq, Deserialize, Serialize)]
239pub struct FootnoteLink {
240 pub id: String,
241 #[serde(rename = "type")]
242 pub kind: FootnoteKind,
243 pub content: Vec<Text>,
244}
245
246#[derive(Debug, PartialEq, Deserialize, Serialize)]
247pub enum FootnoteKind {
248 Note,
249 Comment,
250}
251
252#[derive(Debug, PartialEq, Deserialize, Serialize)]
253pub struct Link {
254 pub href: Href,
255 pub content: Vec<Text>,
256}
257
258#[derive(Debug, PartialEq, Deserialize, Serialize)]
259pub enum Href {
260 Remote(Url),
261 Local(String),
262}
263
264impl AsRef<str> for Href {
265 fn as_ref(&self) -> &str {
266 match self {
267 Href::Remote(url) => url.as_str(),
268 Href::Local(id) => id,
269 }
270 }
271}
272
273#[derive(Debug, PartialEq, Deserialize, Serialize)]
274pub struct InlineImage {
275 pub id: Uuid,
276 #[serde(skip_serializing_if = "Option::is_none")]
277 pub alt: Option<String>,
278}
279
280#[derive(Debug, PartialEq, Deserialize, Serialize)]
281pub struct Text {
282 #[serde(skip_serializing_if = "Option::is_none")]
283 pub font_weight: Option<u16>,
284 #[serde(default, skip_serializing_if = "HashSet::is_empty")]
285 pub font_style: HashSet<FontStyle>,
286 #[serde(default, skip_serializing_if = "HashSet::is_empty")]
287 pub decorations: HashSet<TextDecoration>,
288 #[serde(skip_serializing_if = "Option::is_none")]
289 pub baseline_shift: Option<BaselineShift>,
290 pub value: String,
291}
292
293impl From<String> for Text {
294 fn from(value: String) -> Self {
295 Text {
296 font_weight: None,
297 font_style: HashSet::new(),
298 decorations: HashSet::new(),
299 baseline_shift: None,
300 value,
301 }
302 }
303}
304
305#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
306pub enum FontStyle {
307 Italic,
308 Code,
309}
310
311#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
312pub enum TextDecoration {
313 LineThrough,
314}
315
316#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
317pub enum BaselineShift {
318 Subscript,
319 Superscript,
320}