1use serde::{Deserialize, Serialize};
2
3pub const WDF_SCHEMA: &str = "weimo.document";
5
6pub const WDF_VERSION: u32 = 1;
8
9#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
21#[serde(deny_unknown_fields)]
22pub struct WdfDocument {
23 pub schema: String,
25 pub version: u32,
27 pub root: WdfRoot,
29}
30
31#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
33#[serde(deny_unknown_fields)]
34pub struct WdfRoot {
35 #[serde(rename = "type")]
37 pub node_type: String,
38 pub content: Vec<WdfBlock>,
40}
41
42#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
47#[serde(tag = "type")]
48pub enum WdfBlock {
49 #[serde(rename = "paragraph")]
51 Paragraph { content: Vec<WdfText> },
52 #[serde(rename = "heading")]
54 Heading {
55 attrs: HeadingAttrs,
56 content: Vec<WdfText>,
57 },
58 #[serde(rename = "blockquote")]
60 Blockquote { content: Vec<WdfParagraph> },
61 #[serde(rename = "bullet_list")]
63 BulletList { content: Vec<WdfListItem> },
64 #[serde(rename = "ordered_list")]
67 OrderedList {
68 #[serde(default)]
69 attrs: Option<OrderedListAttrs>,
70 content: Vec<WdfListItem>,
71 },
72}
73
74#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
76#[serde(deny_unknown_fields)]
77pub struct HeadingAttrs {
78 pub level: u8,
79}
80
81#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
83#[serde(deny_unknown_fields)]
84pub struct OrderedListAttrs {
85 pub start: u32,
86}
87
88#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
90#[serde(deny_unknown_fields)]
91pub struct WdfParagraph {
92 #[serde(rename = "type")]
94 pub node_type: String,
95 pub content: Vec<WdfText>,
96}
97
98#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
100#[serde(deny_unknown_fields)]
101pub struct WdfListItem {
102 #[serde(rename = "type")]
104 pub node_type: String,
105 pub content: Vec<WdfParagraph>,
106}
107
108#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
110#[serde(deny_unknown_fields)]
111pub struct WdfText {
112 #[serde(rename = "type")]
114 pub node_type: String,
115 pub text: String,
116 #[serde(default, skip_serializing_if = "Option::is_none")]
118 pub marks: Option<Vec<WdfMark>>,
119}
120
121#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
123#[serde(deny_unknown_fields)]
124pub struct WdfMark {
125 #[serde(rename = "type")]
126 pub mark_type: String,
127}
128
129impl WdfDocument {
130 pub fn empty() -> Self {
132 Self {
133 schema: WDF_SCHEMA.to_string(),
134 version: WDF_VERSION,
135 root: WdfRoot {
136 node_type: "doc".to_string(),
137 content: vec![WdfBlock::Paragraph { content: vec![] }],
138 },
139 }
140 }
141}
142
143impl WdfText {
144 pub fn plain(text: impl Into<String>) -> Self {
146 Self {
147 node_type: "text".to_string(),
148 text: text.into(),
149 marks: None,
150 }
151 }
152
153 pub fn bold(text: impl Into<String>) -> Self {
155 Self {
156 node_type: "text".to_string(),
157 text: text.into(),
158 marks: Some(vec![WdfMark {
159 mark_type: "bold".to_string(),
160 }]),
161 }
162 }
163
164 pub fn is_bold(&self) -> bool {
166 self.marks
167 .as_ref()
168 .is_some_and(|marks| marks.iter().any(|mark| mark.mark_type == "bold"))
169 }
170}
171
172pub fn paragraph(content: Vec<WdfText>) -> WdfParagraph {
174 WdfParagraph {
175 node_type: "paragraph".to_string(),
176 content,
177 }
178}
179
180pub fn list_item(content: Vec<WdfText>) -> WdfListItem {
182 WdfListItem {
183 node_type: "list_item".to_string(),
184 content: vec![paragraph(content)],
185 }
186}