json_book/
lib.rs

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    // TODO: exclude images, something else?
138    #[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    // TODO: exclude images, something else?
154    #[serde(skip_serializing_if = "Option::is_none")]
155    pub subtitle: Option<Paragraph>,
156    // TODO: exclude images, something else?
157    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    // TODO: exclude images, something else?
165    #[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// TODO: exclude images and other non-title items
185#[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}