1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
4#[serde(untagged)]
5pub enum Component {
6 InlineComponent(InlineComponent),
7 BlockComponent(BlockComponent),
8}
9
10#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
11#[serde(untagged)]
12pub enum InlineComponent {
13 Text(Text),
14 Icon(Icon),
15}
16
17#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
18#[serde(untagged)]
19pub enum BlockComponent {
20 Heading(Heading),
21 Paragraph(Paragraph),
22 ListItem(ListItem),
23 List(List),
24 BlockQuote(BlockQuote),
25 Callout(Callout),
26 Divider(Divider),
27 Toggle(Toggle),
28 Bookmark(Bookmark),
29 File(File),
30 Image(Image),
31 CodeBlock(CodeBlock),
32 Katex(Katex),
33 Table(Table),
34 TableRow(TableRow),
35 TableCell(TableCell),
36}
37
38#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
40pub struct Text {
41 pub r#type: String,
43 pub inline: bool,
45
46 pub props: TextProps,
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub slots: Option<TextSlots>,
50}
51
52#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
53pub struct TextProps {
54 pub text: String,
55
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub color: Option<String>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub background_color: Option<String>,
61
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub bold: Option<bool>,
64
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub italic: Option<bool>,
67
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub underline: Option<bool>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
72 pub strikethrough: Option<bool>,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub katex: Option<bool>,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub code: Option<bool>,
79
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub ruby: Option<String>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
84 pub href: Option<String>,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub favicon: Option<String>,
88}
89
90#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
91pub struct TextSlots;
92
93#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
95pub struct Icon {
96 pub r#type: String,
98
99 pub inline: bool,
101
102 pub props: IconProps,
103
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub slots: Option<IconSlots>,
107}
108
109#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
110pub struct IconProps {
111 pub src: String,
112
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub alt: Option<String>,
115}
116
117#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
118pub struct IconSlots;
119
120#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
122pub struct Heading {
123 pub r#type: String,
125
126 pub inline: bool,
128
129 pub props: HeadingProps,
130
131 pub slots: HeadingSlots,
132}
133
134#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
135#[serde(rename_all = "lowercase")]
136#[serde(try_from = "u8", into = "u8")]
137pub enum HeadingLevel {
138 #[default]
139 H1,
140 H2,
141 H3,
142 H4,
143 H5,
144 H6,
145}
146
147impl From<HeadingLevel> for u8 {
148 fn from(level: HeadingLevel) -> Self {
149 match level {
150 HeadingLevel::H1 => 1,
151 HeadingLevel::H2 => 2,
152 HeadingLevel::H3 => 3,
153 HeadingLevel::H4 => 4,
154 HeadingLevel::H5 => 5,
155 HeadingLevel::H6 => 6,
156 }
157 }
158}
159
160impl TryFrom<u8> for HeadingLevel {
161 type Error = String;
162
163 fn try_from(value: u8) -> Result<Self, Self::Error> {
164 match value {
165 1 => Ok(HeadingLevel::H1),
166 2 => Ok(HeadingLevel::H2),
167 3 => Ok(HeadingLevel::H3),
168 4 => Ok(HeadingLevel::H4),
169 5 => Ok(HeadingLevel::H5),
170 6 => Ok(HeadingLevel::H6),
171 _ => Err(format!("Invalid heading level: {}", value)),
172 }
173 }
174}
175
176#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
177pub struct HeadingProps {
178 pub level: HeadingLevel,
179}
180
181#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
182pub struct HeadingSlots {
183 pub default: Vec<InlineComponent>,
184}
185
186#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
188pub struct Paragraph {
189 pub r#type: String,
191
192 pub inline: bool,
194
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub props: Option<ParagraphProps>,
198
199 pub slots: ParagraphSlots,
200}
201
202#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
203pub struct ParagraphProps;
204
205#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
206pub struct ParagraphSlots {
207 pub default: Vec<InlineComponent>,
208}
209
210#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
212pub struct ListItem {
213 pub r#type: String,
214 pub inline: bool,
215 #[serde(skip_serializing_if = "Option::is_none")]
216 pub props: Option<ListItemProps>,
217 pub slots: ListItemSlots,
218}
219
220#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
221pub struct ListItemProps;
222
223#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
224pub struct ListItemSlots {
225 pub default: Vec<InlineComponent>,
226}
227
228#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
230pub struct List {
231 pub r#type: String,
232 pub inline: bool,
233 #[serde(skip_serializing_if = "Option::is_none")]
234 pub props: Option<ListProps>,
235 pub slots: ListSlots,
236}
237
238#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
239pub struct ListProps {
240 #[serde(skip_serializing_if = "Option::is_none")]
241 pub list_style: Option<ListStyle>,
242}
243
244#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
245#[serde(rename_all = "lowercase")]
246pub enum ListStyle {
247 #[default]
248 Unordered,
249 Ordered,
250}
251
252#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
253pub struct ListSlots {
254 pub default: Vec<ListItem>,
255}
256
257#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
259pub struct BlockQuote {
260 pub r#type: String,
261 pub inline: bool,
262 #[serde(skip_serializing_if = "Option::is_none")]
263 pub props: Option<BlockQuoteProps>,
264 pub slots: BlockQuoteSlots,
265}
266
267#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
268pub struct BlockQuoteProps {
269 #[serde(skip_serializing_if = "Option::is_none")]
270 pub cite: Option<String>,
271}
272
273#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
274pub struct BlockQuoteSlots {
275 pub default: Vec<Component>,
276}
277
278#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
280pub struct Callout {
281 pub r#type: String,
282 pub inline: bool,
283 #[serde(skip_serializing_if = "Option::is_none")]
284 pub props: Option<CalloutProps>,
285 pub slots: CalloutSlots,
286}
287
288#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
289#[serde(rename_all = "lowercase")]
290pub enum CalloutType {
291 #[default]
292 Note,
293 Tip,
294 Important,
295 Warning,
296 Caution,
297}
298
299#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
300pub struct CalloutProps {
301 #[serde(skip_serializing_if = "Option::is_none")]
302 pub r#type: Option<CalloutType>,
303}
304
305#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
306pub struct CalloutSlots {
307 pub default: Vec<Component>,
308}
309
310#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
312pub struct Divider {
313 pub r#type: String,
314 pub inline: bool,
315 #[serde(skip_serializing_if = "Option::is_none")]
316 pub props: Option<DividerProps>,
317 #[serde(skip_serializing_if = "Option::is_none")]
318 pub slots: Option<DividerSlots>,
319}
320
321#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
322pub struct DividerProps;
323
324#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
325pub struct DividerSlots;
326
327#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
329pub struct Toggle {
330 pub r#type: String,
331 pub inline: bool,
332 #[serde(skip_serializing_if = "Option::is_none")]
333 pub props: Option<ToggleProps>,
334 pub slots: ToggleSlots,
335}
336
337#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
338pub struct ToggleProps;
339
340#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
341pub struct ToggleSlots {
342 pub default: Vec<Component>,
343 pub summary: Vec<InlineComponent>,
344}
345
346#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
348pub struct Bookmark {
349 pub r#type: String,
350 pub inline: bool,
351 pub props: BookmarkProps,
352 #[serde(skip_serializing_if = "Option::is_none")]
353 pub slots: Option<BookmarkSlots>,
354}
355
356#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
357pub struct BookmarkProps {
358 pub url: String,
359 #[serde(skip_serializing_if = "Option::is_none")]
360 pub title: Option<String>,
361 #[serde(skip_serializing_if = "Option::is_none")]
362 pub description: Option<String>,
363 #[serde(skip_serializing_if = "Option::is_none")]
364 pub image: Option<String>,
365}
366
367#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
368pub struct BookmarkSlots;
369
370#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
372pub struct File {
373 pub r#type: String,
374 pub inline: bool,
375 pub props: FileProps,
376 #[serde(skip_serializing_if = "Option::is_none")]
377 pub slots: Option<FileSlots>,
378}
379
380#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
381pub struct FileProps {
382 pub src: String,
383 #[serde(skip_serializing_if = "Option::is_none")]
384 pub name: Option<String>,
385}
386
387#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
388pub struct FileSlots;
389
390#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
392pub struct Image {
393 pub r#type: String,
394 pub inline: bool,
395 pub props: ImageProps,
396 #[serde(skip_serializing_if = "Option::is_none")]
397 pub slots: Option<ImageSlots>,
398}
399
400#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
401pub struct ImageProps {
402 pub src: String,
403 #[serde(skip_serializing_if = "Option::is_none")]
404 pub alt: Option<String>,
405}
406
407#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
408pub struct ImageSlots;
409
410#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
412pub struct CodeBlock {
413 pub r#type: String,
414 pub inline: bool,
415 pub props: CodeBlockProps,
416 pub slots: CodeBlockSlots,
417}
418
419#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
420pub struct CodeBlockProps {
421 pub code: String,
422 pub language: String,
423}
424
425#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
426pub struct CodeBlockSlots {
427 pub default: Vec<InlineComponent>,
428}
429
430#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
432pub struct Katex {
433 pub r#type: String,
434 pub inline: bool,
435 pub props: KatexProps,
436 #[serde(skip_serializing_if = "Option::is_none")]
437 pub slots: Option<KatexSlots>,
438}
439
440#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
441pub struct KatexProps {
442 pub expression: String,
443}
444
445#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
446pub struct KatexSlots;
447
448#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
450pub struct Table {
451 pub r#type: String,
452 pub inline: bool,
453 #[serde(skip_serializing_if = "Option::is_none")]
454 pub props: Option<TableProps>,
455 pub slots: TableSlots,
456}
457
458#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
459pub struct TableProps {
460 #[serde(skip_serializing_if = "Option::is_none")]
461 pub has_column_header: Option<bool>,
462 #[serde(skip_serializing_if = "Option::is_none")]
463 pub has_row_header: Option<bool>,
464 #[serde(skip_serializing_if = "Option::is_none")]
465 pub caption: Option<String>,
466}
467
468#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
469pub struct TableSlots {
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub header: Option<Vec<TableRow>>,
472 pub body: Vec<TableRow>,
473}
474
475#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
477pub struct TableRow {
478 pub r#type: String,
479 pub inline: bool,
480 #[serde(skip_serializing_if = "Option::is_none")]
481 pub props: Option<TableRowProps>,
482 pub slots: TableRowSlots,
483}
484
485#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
486pub struct TableRowProps;
487
488#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
489pub struct TableRowSlots {
490 pub default: Vec<TableCell>,
491}
492
493#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
495pub struct TableCell {
496 pub r#type: String,
497 pub inline: bool,
498 #[serde(skip_serializing_if = "Option::is_none")]
499 pub props: Option<TableCellProps>,
500 pub slots: TableCellSlots,
501}
502
503#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
504pub struct TableCellProps {
505 #[serde(skip_serializing_if = "Option::is_none")]
506 pub is_header: Option<bool>,
507}
508
509#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
510pub struct TableCellSlots {
511 pub default: Vec<InlineComponent>,
512}