summaryrefslogtreecommitdiffstats
path: root/src/generator/pandoc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/generator/pandoc.rs')
-rw-r--r--src/generator/pandoc.rs166
1 files changed, 166 insertions, 0 deletions
diff --git a/src/generator/pandoc.rs b/src/generator/pandoc.rs
new file mode 100644
index 0000000..1753d34
--- /dev/null
+++ b/src/generator/pandoc.rs
@@ -0,0 +1,166 @@
1use super::config::Config;
2use crate::types::Entity;
3
4use pandoc_types::definition::{Attr, Block, Inline, Meta, MetaValue, Pandoc};
5
6use std::collections::BTreeMap;
7
8pub(crate) fn into_pandoc(
9 entity: Entity,
10 config: &Config,
11) -> (Pandoc, BTreeMap<String, Entity>) {
12 let mut meta = Meta::null();
13
14 let title = vec![Inline::Code(Attr::null(), entity.name.clone())];
15
16 meta.0
17 .insert("title".to_string(), MetaValue::MetaString(entity.name));
18
19 let mut content = Vec::new();
20
21 content.push(Block::Header(1, Attr::null(), title));
22
23 if !entity.documentation.is_empty() {
24 content.push(Block::Header(
25 2,
26 Attr::null(),
27 vec![Inline::Str(String::from("Description"))],
28 ));
29
30 content.push(Block::Div(
31 Attr(String::new(), vec![String::from("doc")], vec![]),
32 vec![raw_markdown(entity.documentation.clone())],
33 ));
34 }
35
36 let mut inline_children = BTreeMap::new();
37 let mut separate_children = BTreeMap::new();
38
39 let entity_kind = entity.kind;
40 for (children_kind, children) in entity.children {
41 if config
42 .inlines
43 .get(&entity.language)
44 .and_then(|lang_inlines| lang_inlines.get(&entity_kind))
45 .map(|children_inlines| children_inlines.contains(&children_kind))
46 == Some(true)
47 {
48 inline_children.insert(children_kind, children);
49 } else {
50 // By default, do not inline
51 separate_children.insert(children_kind, children);
52 }
53 }
54
55 for (section_name, children) in &separate_children {
56 if let Some(members_list) = member_list(children) {
57 content.push(Block::Header(
58 2,
59 Attr::null(),
60 vec![Inline::Str(String::from(section_name))],
61 ));
62
63 content.push(members_list);
64 }
65 }
66
67 let mut embedded_documentation = Vec::new();
68
69 for (section_name, children) in inline_children {
70 if let Some(members_list) = member_list(&children) {
71 content.push(Block::Header(
72 2,
73 Attr::null(),
74 vec![Inline::Str(section_name.clone())],
75 ));
76
77 content.push(members_list);
78
79 embedded_documentation.push(Block::Header(
80 2,
81 Attr::null(),
82 vec![Inline::Str(section_name + " Documentation")],
83 ));
84
85 for (_id, child) in children {
86 embedded_documentation.push(Block::Header(
87 3,
88 Attr::null(),
89 vec![Inline::Code(Attr::null(), child.name)],
90 ));
91
92 embedded_documentation.push(Block::Div(
93 Attr(String::new(), vec![String::from("doc")], vec![]),
94 vec![raw_markdown(child.documentation)],
95 ));
96 }
97 }
98 }
99
100 content.append(&mut embedded_documentation);
101
102 let leftovers = separate_children
103 .into_iter()
104 .map(|(_section_name, children)| children)
105 .flatten()
106 .collect();
107
108 (Pandoc(meta, content), leftovers)
109}
110
111fn str_block(content: String) -> Block {
112 Block::Plain(vec![Inline::Str(content)])
113}
114
115fn entity_link(id: &str, name: String) -> Inline {
116 use pandoc_types::definition::Target;
117 use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
118
119 use std::iter::once;
120
121 // https://url.spec.whatwg.org/#fragment-percent-encode-set
122 const FRAGMENT: &AsciiSet = &CONTROLS
123 .add(b' ')
124 .add(b'"')
125 .add(b'<')
126 .add(b'>')
127 .add(b'`')
128 .add(b'#')
129 .add(b'?')
130 .add(b'{')
131 .add(b'}');
132
133 Inline::Link(
134 Attr::null(),
135 vec![Inline::Code(Attr::null(), name)],
136 Target(
137 once("./")
138 .chain(utf8_percent_encode(id, FRAGMENT))
139 .collect(),
140 String::new(),
141 ),
142 )
143}
144
145fn raw_markdown(text: String) -> Block {
146 use pandoc_types::definition::Format;
147 Block::RawBlock(Format(String::from("markdown")), text)
148}
149
150fn member_list<'a>(members: impl IntoIterator<Item = (&'a String, &'a Entity)>) -> Option<Block> {
151 let definitions: Vec<(Vec<Inline>, Vec<Vec<Block>>)> = members
152 .into_iter()
153 .map(|(id, entity)| {
154 (
155 vec![entity_link(id, entity.name.clone())],
156 vec![vec![str_block(entity.brief_description.clone())]],
157 )
158 })
159 .collect();
160
161 if definitions.is_empty() {
162 None
163 } else {
164 Some(Block::DefinitionList(definitions))
165 }
166}