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