sable-markdown/src/render/util.rs@main
raw
1use pretty::{Arena, DocAllocator, DocBuilder};
2
3pub(super) fn escape(value: &str) -> String {
4 let mut escaped = String::new();
5 for c in value.chars() {
6 match c {
7 '&' => escaped.push_str("&"),
8 '<' => escaped.push_str("<"),
9 '>' => escaped.push_str(">"),
10 '"' => escaped.push_str("""),
11 '\'' => escaped.push_str("'"),
12 _ => escaped.push(c),
13 }
14 }
15 escaped
16}
17
18pub(super) trait ArenaExt<'a> {
19 fn tag(
20 &'a self,
21 tag: &'static str,
22 attributes: Vec<(String, String)>,
23 inner: DocBuilder<'a, Arena<'a>, ()>,
24 ) -> DocBuilder<'a, Arena<'a>, ()>;
25}
26
27impl<'a> ArenaExt<'a> for Arena<'a> {
28 fn tag(
29 &'a self,
30 tag: &'static str,
31 attributes: Vec<(String, String)>,
32 inner: DocBuilder<'a, Self, ()>,
33 ) -> DocBuilder<'a, Self, ()> {
34 let mut attrs = self.nil();
35 for (key, value) in attributes {
36 let attr = if value.is_empty() {
37 self.text(" ").append(self.text(key))
38 } else {
39 self.text(" ")
40 .append(self.text(key))
41 .append(self.text("=\""))
42 .append(self.text(escape(&value)))
43 .append(self.text("\""))
44 };
45
46 attrs = attrs.append(attr);
47 }
48 let open_tag = self
49 .text("<")
50 .append(self.text(tag))
51 .append(attrs)
52 .append(self.text(">"));
53 let close_tag = self
54 .text("</")
55 .append(self.text(tag))
56 .append(self.text(">"));
57 open_tag.append(inner).append(close_tag)
58 }
59}
60