1mod r#macro;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
6#[serde(untagged)]
7pub enum Component {
8 InlineComponent(InlineComponent),
9 BlockComponent(BlockComponent),
10}
11
12#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
13#[serde(tag = "type")]
14pub enum InlineComponent {
15 Text(Text),
16 Icon(Icon),
17}
18
19#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
20#[serde(tag = "type")]
21pub enum BlockComponent {
22 Heading(Heading),
23 Paragraph(Paragraph),
24 ListItem(ListItem),
25 List(List),
26 BlockQuote(BlockQuote),
27 Callout(Callout),
28 Divider(Divider),
29 Toggle(Toggle),
30 Bookmark(Bookmark),
31 File(File),
32 Image(Image),
33 CodeBlock(CodeBlock),
34 Katex(Katex),
35 Table(Table),
36 TableRow(TableRow),
37 TableCell(TableCell),
38}
39
40#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
42pub struct Text {
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
93crate::to_inline_component!(Text);
94
95#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
97pub struct Icon {
98 pub inline: bool,
100
101 pub props: IconProps,
102
103 #[serde(skip_serializing_if = "Option::is_none")]
105 pub slots: Option<IconSlots>,
106}
107
108#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
109pub struct IconProps {
110 pub src: String,
111
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub alt: Option<String>,
114}
115
116#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
117pub struct IconSlots;
118
119crate::to_inline_component!(Icon);
120
121#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
123pub struct Heading {
124 pub inline: bool,
126
127 pub props: HeadingProps,
128
129 pub slots: HeadingSlots,
130}
131
132#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
133#[serde(rename_all = "lowercase")]
134#[serde(try_from = "u8", into = "u8")]
135pub enum HeadingLevel {
136 #[default]
137 H1,
138 H2,
139 H3,
140 H4,
141 H5,
142 H6,
143}
144
145impl From<HeadingLevel> for u8 {
146 fn from(level: HeadingLevel) -> Self {
147 match level {
148 HeadingLevel::H1 => 1,
149 HeadingLevel::H2 => 2,
150 HeadingLevel::H3 => 3,
151 HeadingLevel::H4 => 4,
152 HeadingLevel::H5 => 5,
153 HeadingLevel::H6 => 6,
154 }
155 }
156}
157
158impl TryFrom<u8> for HeadingLevel {
159 type Error = String;
160
161 fn try_from(value: u8) -> Result<Self, Self::Error> {
162 match value {
163 1 => Ok(HeadingLevel::H1),
164 2 => Ok(HeadingLevel::H2),
165 3 => Ok(HeadingLevel::H3),
166 4 => Ok(HeadingLevel::H4),
167 5 => Ok(HeadingLevel::H5),
168 6 => Ok(HeadingLevel::H6),
169 _ => Err(format!("Invalid heading level: {}", value)),
170 }
171 }
172}
173
174#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
175pub struct HeadingProps {
176 pub level: HeadingLevel,
177}
178
179#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
180pub struct HeadingSlots {
181 pub default: Vec<InlineComponent>,
182}
183
184crate::to_block_component!(Heading);
185
186#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
188pub struct Paragraph {
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
207crate::to_block_component!(Paragraph);
208
209#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
211pub struct ListItem {
212 pub inline: bool,
213 #[serde(skip_serializing_if = "Option::is_none")]
214 pub props: Option<ListItemProps>,
215 pub slots: ListItemSlots,
216}
217
218#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
219pub struct ListItemProps;
220
221#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
222pub struct ListItemSlots {
223 pub default: Vec<InlineComponent>,
224}
225
226crate::to_block_component!(ListItem);
227
228#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
230pub struct List {
231 pub inline: bool,
232 #[serde(skip_serializing_if = "Option::is_none")]
233 pub props: Option<ListProps>,
234 pub slots: ListSlots,
235}
236
237#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
238pub struct ListProps {
239 #[serde(skip_serializing_if = "Option::is_none")]
240 pub list_style: Option<ListStyle>,
241}
242
243#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
244#[serde(rename_all = "lowercase")]
245pub enum ListStyle {
246 #[default]
247 Unordered,
248 Ordered,
249}
250
251#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
252pub struct ListSlots {
253 pub default: Vec<ListItem>,
254}
255
256crate::to_block_component!(List);
257
258#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
260pub struct BlockQuote {
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
278crate::to_block_component!(BlockQuote);
279
280#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
282pub struct Callout {
283 pub inline: bool,
284 #[serde(skip_serializing_if = "Option::is_none")]
285 pub props: Option<CalloutProps>,
286 pub slots: CalloutSlots,
287}
288
289#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
290#[serde(rename_all = "lowercase")]
291pub enum CalloutType {
292 #[default]
293 Note,
294 Tip,
295 Important,
296 Warning,
297 Caution,
298}
299
300#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
301pub struct CalloutProps {
302 #[serde(skip_serializing_if = "Option::is_none")]
303 pub r#type: Option<CalloutType>,
304}
305
306#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
307pub struct CalloutSlots {
308 pub default: Vec<Component>,
309}
310
311crate::to_block_component!(Callout);
312
313#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
315pub struct Divider {
316 pub inline: bool,
317 #[serde(skip_serializing_if = "Option::is_none")]
318 pub props: Option<DividerProps>,
319 #[serde(skip_serializing_if = "Option::is_none")]
320 pub slots: Option<DividerSlots>,
321}
322
323#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
324pub struct DividerProps;
325
326#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
327pub struct DividerSlots;
328
329crate::to_block_component!(Divider);
330
331#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
333pub struct Toggle {
334 pub inline: bool,
335 #[serde(skip_serializing_if = "Option::is_none")]
336 pub props: Option<ToggleProps>,
337 pub slots: ToggleSlots,
338}
339
340#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
341pub struct ToggleProps;
342
343#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
344pub struct ToggleSlots {
345 pub default: Vec<Component>,
346 pub summary: Vec<InlineComponent>,
347}
348
349crate::to_block_component!(Toggle);
350
351#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
353pub struct Bookmark {
354 pub inline: bool,
355 pub props: BookmarkProps,
356 #[serde(skip_serializing_if = "Option::is_none")]
357 pub slots: Option<BookmarkSlots>,
358}
359
360#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
361pub struct BookmarkProps {
362 pub url: String,
363 #[serde(skip_serializing_if = "Option::is_none")]
364 pub title: Option<String>,
365 #[serde(skip_serializing_if = "Option::is_none")]
366 pub description: Option<String>,
367 #[serde(skip_serializing_if = "Option::is_none")]
368 pub image: Option<String>,
369}
370
371#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
372pub struct BookmarkSlots;
373
374crate::to_block_component!(Bookmark);
375
376#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
378pub struct File {
379 pub inline: bool,
380 pub props: FileProps,
381 #[serde(skip_serializing_if = "Option::is_none")]
382 pub slots: Option<FileSlots>,
383}
384
385#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
386pub struct FileProps {
387 pub src: String,
388 #[serde(skip_serializing_if = "Option::is_none")]
389 pub name: Option<String>,
390}
391
392#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
393pub struct FileSlots;
394
395crate::to_block_component!(File);
396
397#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
399pub struct Image {
400 pub inline: bool,
401 pub props: ImageProps,
402 #[serde(skip_serializing_if = "Option::is_none")]
403 pub slots: Option<ImageSlots>,
404}
405
406#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
407pub struct ImageProps {
408 pub src: String,
409 #[serde(skip_serializing_if = "Option::is_none")]
410 pub alt: Option<String>,
411}
412
413#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
414pub struct ImageSlots;
415
416crate::to_block_component!(Image);
417
418#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
420pub struct CodeBlock {
421 pub inline: bool,
422 pub props: CodeBlockProps,
423 pub slots: CodeBlockSlots,
424}
425
426#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
427pub struct CodeBlockProps {
428 pub code: String,
429 pub language: String,
430}
431
432#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
433pub struct CodeBlockSlots {
434 pub default: Vec<InlineComponent>,
435}
436
437crate::to_block_component!(CodeBlock);
438
439#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
441pub struct Katex {
442 pub inline: bool,
443 pub props: KatexProps,
444 #[serde(skip_serializing_if = "Option::is_none")]
445 pub slots: Option<KatexSlots>,
446}
447
448#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
449pub struct KatexProps {
450 pub expression: String,
451}
452
453#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
454pub struct KatexSlots;
455
456crate::to_block_component!(Katex);
457
458#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
460pub struct Table {
461 pub inline: bool,
462 #[serde(skip_serializing_if = "Option::is_none")]
463 pub props: Option<TableProps>,
464 pub slots: TableSlots,
465}
466
467#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
468pub struct TableProps {
469 #[serde(skip_serializing_if = "Option::is_none")]
470 pub has_column_header: Option<bool>,
471 #[serde(skip_serializing_if = "Option::is_none")]
472 pub has_row_header: Option<bool>,
473 #[serde(skip_serializing_if = "Option::is_none")]
474 pub caption: Option<String>,
475}
476
477#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
478pub struct TableSlots {
479 #[serde(skip_serializing_if = "Option::is_none")]
480 pub header: Option<Vec<TableRow>>,
481 pub body: Vec<TableRow>,
482}
483
484crate::to_block_component!(Table);
485
486#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
488pub struct TableRow {
489 pub inline: bool,
490 #[serde(skip_serializing_if = "Option::is_none")]
491 pub props: Option<TableRowProps>,
492 pub slots: TableRowSlots,
493}
494
495#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
496pub struct TableRowProps;
497
498#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
499pub struct TableRowSlots {
500 pub default: Vec<TableCell>,
501}
502
503crate::to_block_component!(TableRow);
504
505#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
507pub struct TableCell {
508 pub inline: bool,
509 #[serde(skip_serializing_if = "Option::is_none")]
510 pub props: Option<TableCellProps>,
511 pub slots: TableCellSlots,
512}
513
514#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
515pub struct TableCellProps {
516 #[serde(skip_serializing_if = "Option::is_none")]
517 pub is_header: Option<bool>,
518}
519
520#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
521pub struct TableCellSlots {
522 pub default: Vec<InlineComponent>,
523}
524
525crate::to_block_component!(TableCell);