mdiu/
html.rs

1use super::{Block, Level, Markup};
2
3/// An HTML formatter, available with the `html` feature
4///
5/// Links are formatted as lists of links.
6pub struct Html;
7
8impl Markup for Html {
9    fn markup(blocks: &[Block]) -> String {
10        let mut iter = blocks.iter();
11        let mut block = iter.next();
12        let mut in_list: Option<List> = None;
13
14        std::iter::from_fn(|| {
15            let intermediate = match (in_list.as_ref(), block) {
16                (None, Some(Block::Link(_))) => {
17                    in_list = Some(List::Link);
18                    Intermediate::OpenList
19                }
20                (None, Some(Block::ListItem(_))) => {
21                    in_list = Some(List::Item);
22                    Intermediate::OpenList
23                }
24                (Some(_), None) => {
25                    in_list = None;
26                    Intermediate::CloseList
27                }
28                (Some(List::Link), Some(b)) if !matches!(b, Block::Link(_)) => {
29                    in_list = None;
30                    Intermediate::CloseList
31                }
32                (Some(List::Item), Some(b)) if !matches!(b, Block::ListItem(_)) => {
33                    in_list = None;
34                    Intermediate::CloseList
35                }
36                (_, Some(block)) => Intermediate::Block(block),
37                (None, None) => return None,
38            };
39
40            if let Intermediate::Block(_) = intermediate {
41                block = iter.next();
42            }
43
44            Some(intermediate)
45        })
46        .map(|intermediate| match intermediate {
47            Intermediate::Block(block) => generate(block),
48            Intermediate::OpenList => "<ul>\n".to_string(),
49            Intermediate::CloseList => "</ul>\n".to_string(),
50        })
51        .collect::<String>()
52    }
53}
54
55enum List {
56    Link,
57    Item,
58}
59
60enum Intermediate<'a> {
61    Block(&'a Block),
62    OpenList,
63    CloseList,
64}
65
66fn generate(block: &Block) -> String {
67    match block {
68        Block::Text(text) => format!("<p>{}</p>\n", text),
69        Block::Link(link) => match link.label() {
70            Some(label) => {
71                format!("<li><a href=\"{}\">{}</a></li>\n", link.uri(), label)
72            }
73            None => {
74                format!("<li><a href=\"{0}\">{0}</a></li>\n", link.uri())
75            }
76        },
77        Block::Heading(Level::One, text) => format!("<h1>{}</h1>\n", text),
78        Block::Heading(Level::Two, text) => format!("<h2>{}</h2>\n", text),
79        Block::Heading(Level::Three, text) => format!("<h3>{}</h3>\n", text),
80        Block::ListItem(text) => {
81            format!("<li>{}</li>\n", text)
82        }
83        Block::Quote(text) => format!("<blockquote>{}</blockquote>\n", text),
84        Block::Preformatted(pre) => format!("<pre>\n{}\n</pre>\n", pre.text()),
85        // TODO
86        Block::Empty => "".to_string(),
87    }
88}