1use std::borrow::Cow;
4
5use crate::model::{html::*, tree::*};
6
7#[derive(Debug, Clone)]
9pub struct Transformer {
10 #[allow(dead_code)]
11 section: bool,
12}
13
14#[allow(clippy::derivable_impls)]
15impl Default for Transformer {
16 fn default() -> Self {
17 Self { section: false }
18 }
19}
20
21impl Transformer {
22 pub fn new() -> Self {
24 Self::default()
25 }
26
27 #[allow(dead_code)]
28 fn section(mut self, section: bool) -> Self {
29 self.section = section;
30 self
31 }
32}
33
34impl Transformer {
35 pub fn transform<'a>(&self, tree: MarkdownTree<'a>) -> DocumentNode<'a> {
37 DocumentNode {
38 root: self.block_tree(tree.root),
39 }
40 }
41
42 fn block_tree<'a>(&self, tree: BlockTree<'a>) -> Vec<Node<'a>> {
43 tree.root
44 .into_iter()
45 .map(|item| self.block_item(item))
46 .collect()
47 }
48
49 fn block_item<'a>(&self, item: BlockItem<'a>) -> Node<'a> {
50 match item {
51 BlockItem::Paragraph(tree) => self.paragraph(tree),
52 BlockItem::Headline(level, tree) => self.headline(level, tree),
53 BlockItem::BulletList(tree) => self.bullet_list(tree),
54 BlockItem::OrderedList(tree) => self.ordered_list(tree),
55 BlockItem::BlockQuote(tree) => self.blockquote(tree),
56 BlockItem::Container(_, _) => todo!(),
57 }
58 }
59
60 fn paragraph<'a>(&self, tree: InlineTree<'a>) -> Node<'a> {
61 Node::Element(ElementNode {
62 tag: ElementTag::P,
63 children: self.inline_tree(tree),
64 ..Default::default()
65 })
66 }
67
68 fn headline<'a>(&self, level: u8, tree: InlineTree<'a>) -> Node<'a> {
69 Node::Element(ElementNode {
70 tag: ElementTag::headline(level).unwrap(),
71 children: self.inline_tree(tree),
72 ..Default::default()
73 })
74 }
75
76 fn bullet_list<'a>(&self, tree: ListTree<'a>) -> Node<'a> {
77 Node::Element(ElementNode {
78 tag: ElementTag::Ul,
79 children: self.list_tree(tree),
80 ..Default::default()
81 })
82 }
83
84 fn ordered_list<'a>(&self, tree: ListTree<'a>) -> Node<'a> {
85 Node::Element(ElementNode {
86 tag: ElementTag::Ol,
87 children: self.list_tree(tree),
88 ..Default::default()
89 })
90 }
91
92 fn blockquote<'a>(&self, tree: BlockTree<'a>) -> Node<'a> {
93 Node::Element(ElementNode {
94 tag: ElementTag::Blockquote,
95 children: self.block_tree(tree),
96 ..Default::default()
97 })
98 }
99
100 fn list_tree<'a>(&self, tree: ListTree<'a>) -> Vec<Node<'a>> {
101 tree.root
102 .into_iter()
103 .map(|item| {
104 let mut nodes = self.inline_tree(item.name);
105
106 item.children
107 .into_iter()
108 .map(|item| self.block_item(item))
109 .for_each(|node| nodes.push(node));
110
111 Node::Element(ElementNode {
112 tag: ElementTag::Li,
113 children: nodes,
114 ..Default::default()
115 })
116 })
117 .collect()
118 }
119
120 fn inline_tree<'a>(&self, tree: InlineTree<'a>) -> Vec<Node<'a>> {
121 tree.root
122 .into_iter()
123 .map(|item| self.inline_item(item))
124 .collect()
125 }
126
127 fn inline_item<'a>(&self, item: InlineItem<'a>) -> Node<'a> {
128 match item {
129 InlineItem::Text(text) => self.text(text),
130 InlineItem::Italic(tree) => self.italic(tree),
131 InlineItem::Strong(tree) => self.strong(tree),
132 InlineItem::Break => self.r#break(),
133 }
134 }
135
136 fn text<'a>(&self, text: Cow<'a, str>) -> Node<'a> {
137 Node::Text(TextNode { text })
138 }
139
140 fn italic<'a>(&self, tree: InlineTree<'a>) -> Node<'a> {
141 Node::Element(ElementNode {
142 tag: ElementTag::Em,
143 children: self.inline_tree(tree),
144 ..Default::default()
145 })
146 }
147
148 fn strong<'a>(&self, tree: InlineTree<'a>) -> Node<'a> {
149 Node::Element(ElementNode {
150 tag: ElementTag::Strong,
151 children: self.inline_tree(tree),
152 ..Default::default()
153 })
154 }
155
156 fn r#break<'a>(&self) -> Node<'a> {
157 Node::Element(ElementNode {
158 tag: ElementTag::Br,
159 ..Default::default()
160 })
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn test_transform() {
170 let tree = MarkdownTree {
175 root: BlockTree {
176 root: vec![
177 BlockItem::Headline(
178 1,
179 InlineTree {
180 root: vec![
181 InlineItem::Text(Cow::Borrowed("Hello")),
182 InlineItem::Break,
183 InlineItem::Text(Cow::Borrowed("World")),
184 ],
185 },
186 ),
187 BlockItem::Paragraph(InlineTree {
188 root: vec![InlineItem::Strong(InlineTree {
189 root: vec![InlineItem::Text(Cow::Borrowed("Hello World2"))],
190 })],
191 }),
192 ],
193 },
194 };
195
196 let transformer = Transformer::new();
197 let document = transformer.transform(tree);
198
199 assert_eq!(
200 document,
201 DocumentNode {
202 root: vec![
203 Node::Element(ElementNode {
204 tag: ElementTag::H1,
205 children: vec![
206 Node::Text(TextNode {
207 text: Cow::Borrowed("Hello")
208 }),
209 Node::Element(ElementNode {
210 tag: ElementTag::Br,
211 ..Default::default()
212 }),
213 Node::Text(TextNode {
214 text: Cow::Borrowed("World")
215 }),
216 ],
217 ..Default::default()
218 }),
219 Node::Element(ElementNode {
220 tag: ElementTag::P,
221 children: vec![Node::Element(ElementNode {
222 tag: ElementTag::Strong,
223 children: vec![Node::Text(TextNode {
224 text: Cow::Borrowed("Hello World2")
225 })],
226 ..Default::default()
227 }),],
228 ..Default::default()
229 }),
230 ]
231 }
232 );
233 }
234
235 #[test]
236 fn test_transform2() {
237 #[rustfmt::skip]
238 let tree = MarkdownTree {
245 root: BlockTree {
246 root: vec![BlockItem::BulletList(ListTree {
247 root: vec![
248 ListItem {
249 name: InlineTree {
250 root: vec![InlineItem::Text(Cow::Borrowed("Hello"))],
251 },
252 children: vec![],
253 },
254 ListItem {
255 name: InlineTree {
256 root: vec![InlineItem::Text(Cow::Borrowed("World"))],
257 },
258 children: vec![
259 BlockItem::OrderedList(ListTree {
260 root: vec![
261 ListItem {
262 name: InlineTree {
263 root: vec![InlineItem::Text(Cow::Borrowed(
264 "Change the ",
265 ))],
266 },
267 children: vec![],
268 },
269 ListItem {
270 name: InlineTree {
271 root: vec![InlineItem::Strong(InlineTree {
272 root: vec![InlineItem::Text(Cow::Borrowed(
273 "world",
274 ))],
275 })],
276 },
277 children: vec![],
278 },
279 ListItem {
280 name: InlineTree {
281 root: vec![
282 InlineItem::Text(Cow::Borrowed("OK")),
283 InlineItem::Break,
284 InlineItem::Text(Cow::Borrowed("Good")),
285 ],
286 },
287 children: vec![],
288 },
289 ],
290 }),
291 BlockItem::Paragraph(InlineTree {
292 root: vec![InlineItem::Text(Cow::Borrowed("OK"))],
293 }),
294 ],
295 },
296 ListItem {
297 name: InlineTree {
298 root: vec![InlineItem::Text(Cow::Borrowed("Hello2"))],
299 },
300 children: vec![],
301 },
302 ],
303 })],
304 },
305 };
306
307 let transformer = Transformer::new();
308 let document = transformer.transform(tree);
309
310 assert_eq!(
311 document,
312 DocumentNode {
313 root: vec![Node::Element(ElementNode {
314 tag: ElementTag::Ul,
315 children: vec![
316 Node::Element(ElementNode {
317 tag: ElementTag::Li,
318 children: vec![Node::Text(TextNode {
319 text: Cow::Borrowed("Hello")
320 }),],
321 ..Default::default()
322 }),
323 Node::Element(ElementNode {
324 tag: ElementTag::Li,
325 children: vec![
326 Node::Text(TextNode {
327 text: Cow::Borrowed("World")
328 }),
329 Node::Element(ElementNode {
330 tag: ElementTag::Ol,
331 children: vec![
332 Node::Element(ElementNode {
333 tag: ElementTag::Li,
334 children: vec![Node::Text(TextNode {
335 text: Cow::Borrowed("Change the ")
336 }),],
337 ..Default::default()
338 }),
339 Node::Element(ElementNode {
340 tag: ElementTag::Li,
341 children: vec![Node::Element(ElementNode {
342 tag: ElementTag::Strong,
343 children: vec![Node::Text(TextNode {
344 text: Cow::Borrowed("world")
345 }),],
346 ..Default::default()
347 }),],
348 ..Default::default()
349 }),
350 Node::Element(ElementNode {
351 tag: ElementTag::Li,
352 children: vec![
353 Node::Text(TextNode {
354 text: Cow::Borrowed("OK")
355 }),
356 Node::Element(ElementNode {
357 tag: ElementTag::Br,
358 ..Default::default()
359 }),
360 Node::Text(TextNode {
361 text: Cow::Borrowed("Good")
362 }),
363 ],
364 ..Default::default()
365 }),
366 ],
367 ..Default::default()
368 }),
369 Node::Element(ElementNode {
370 tag: ElementTag::P,
371 children: vec![Node::Text(TextNode {
372 text: Cow::Borrowed("OK")
373 }),],
374 ..Default::default()
375 }),
376 ],
377 ..Default::default()
378 }),
379 Node::Element(ElementNode {
380 tag: ElementTag::Li,
381 children: vec![Node::Text(TextNode {
382 text: Cow::Borrowed("Hello2")
383 }),],
384 ..Default::default()
385 }),
386 ],
387 ..Default::default()
388 }),]
389 }
390 )
391 }
392}