elmethis_json_component_types/
lib.rs

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// Text # -------------------------------------------------- #
41#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
42pub struct Text {
43    /// Always `true`
44    pub inline: bool,
45
46    pub props: TextProps,
47    // Always `None`
48    #[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// Icon # -------------------------------------------------- #
96#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
97pub struct Icon {
98    /// Always `true`
99    pub inline: bool,
100
101    pub props: IconProps,
102
103    // Always `None`
104    #[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// Heading # -------------------------------------------------- #
122#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
123pub struct Heading {
124    /// Always `false`
125    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// Paragraph # -------------------------------------------------- #
187#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Default)]
188pub struct Paragraph {
189    /// Always `false`
190    pub inline: bool,
191
192    // Always `None`
193    #[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// ListItem # -------------------------------------------------- #
210#[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// List # -------------------------------------------------- #
229#[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// BlockQuote # -------------------------------------------------- #
259#[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// Callout # -------------------------------------------------- #
281#[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// Divider # -------------------------------------------------- #
314#[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// Toggle # -------------------------------------------------- #
332#[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// Bookmark # -------------------------------------------------- #
352#[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// File # -------------------------------------------------- #
377#[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// Image # -------------------------------------------------- #
398#[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// CodeBlock # -------------------------------------------------- #
419#[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// Katex # -------------------------------------------------- #
440#[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// Table # -------------------------------------------------- #
459#[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// TableRow # -------------------------------------------------- #
487#[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// TableCell # -------------------------------------------------- #
506#[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);