summaryrefslogtreecommitdiffstats
path: root/src/pandoc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/pandoc.rs')
-rw-r--r--src/pandoc.rs246
1 files changed, 246 insertions, 0 deletions
diff --git a/src/pandoc.rs b/src/pandoc.rs
new file mode 100644
index 0000000..5ca84e7
--- /dev/null
+++ b/src/pandoc.rs
@@ -0,0 +1,246 @@
1//mod types;
2
3use crate::entities::*;
4
5use pandoc_types::definition::{Attr, Block, Inline, Meta, MetaValue, Pandoc};
6
7use std::collections::HashMap;
8
9impl Described<Entity> {
10 pub fn into_pandoc(
11 self,
12 descriptions: &HashMap<Usr, Description>,
13 ) -> (Pandoc, HashMap<Usr, Entity>) {
14 let mut meta = Meta::null();
15
16 let title = self.title();
17 let mut content_before = self.content_before(&descriptions);
18 let mut content_after = self.content_after(&descriptions);
19 let leftovers = self.leftovers();
20
21 meta.0.insert(
22 "title".to_string(),
23 MetaValue::MetaString(self.description.name),
24 );
25
26 let mut content = Vec::new();
27
28 content.push(Block::Header(1, Attr::null(), title));
29
30 content.append(&mut content_before);
31
32 if !self.description.detailed.is_empty() {
33 content.push(Block::Header(
34 2,
35 Attr::null(),
36 vec![Inline::Str(String::from("Description"))],
37 ));
38
39 content.push(Block::Div(
40 Attr(String::new(), vec![String::from("doc")], vec![]),
41 vec![raw_markdown(self.description.detailed)],
42 ));
43 }
44
45 content.append(&mut content_after);
46
47 (Pandoc(meta, content), leftovers)
48 }
49}
50
51// TODO: replace with single function so we can move out, and remove all of those clones
52trait PandocDisplay {
53 fn content_before(&self, _descriptions: &HashMap<Usr, Description>) -> Vec<Block> {
54 vec![]
55 }
56
57 fn content_after(&self, _descriptions: &HashMap<Usr, Description>) -> Vec<Block> {
58 vec![]
59 }
60
61 fn leftovers(&self) -> HashMap<Usr, Entity> {
62 HashMap::new()
63 }
64}
65
66impl Described<Entity> {
67 fn title(&self) -> Vec<Inline> {
68 match &self.entity {
69 Entity::Variable(variable) => vec![Inline::Code(
70 Attr(String::new(), vec![String::from("cpp")], vec![]),
71 variable.r#type.clone() + " " + &self.description.name,
72 )],
73 _ => vec![Inline::Code(Attr::null(), self.description.name.clone())],
74 }
75 }
76}
77
78impl PandocDisplay for Described<Entity> {
79 fn content_before(&self, descriptions: &HashMap<Usr, Description>) -> Vec<Block> {
80 match &self.entity {
81 Entity::NameSpace(ns) => ns.content_before(descriptions),
82 Entity::Variable(var) => var.content_before(descriptions),
83 Entity::Function(func) => func.content_before(descriptions),
84 Entity::Class(class) => class.content_before(descriptions),
85 }
86 }
87
88 fn content_after(&self, descriptions: &HashMap<Usr, Description>) -> Vec<Block> {
89 match &self.entity {
90 Entity::NameSpace(ns) => ns.content_after(descriptions),
91 Entity::Variable(var) => var.content_after(descriptions),
92 Entity::Function(func) => func.content_after(descriptions),
93 Entity::Class(class) => class.content_after(descriptions),
94 }
95 }
96
97 fn leftovers(&self) -> HashMap<Usr, Entity> {
98 match &self.entity {
99 Entity::NameSpace(ns) => ns.leftovers(),
100 Entity::Variable(var) => var.leftovers(),
101 Entity::Function(func) => func.leftovers(),
102 Entity::Class(class) => class.leftovers(),
103 }
104 }
105}
106
107impl PandocDisplay for NameSpace {
108 fn content_after(&self, descriptions: &HashMap<Usr, Description>) -> Vec<Block> {
109 let mut content = Vec::new();
110
111 content.push(Block::Header(
112 2,
113 Attr::null(),
114 vec![Inline::Str("Members".to_string())],
115 ));
116
117 if let Some(member_list) = member_list(self.members.keys(), descriptions) {
118 content.push(member_list);
119 } else {
120 content.push(str_block(String::from("None")));
121 }
122
123 content
124 }
125
126 fn leftovers(&self) -> HashMap<Usr, Entity> {
127 self.members.clone()
128 }
129}
130
131impl PandocDisplay for Class {
132 fn content_after(&self, descriptions: &HashMap<Usr, Description>) -> Vec<Block> {
133 let mut content = Vec::new();
134
135 if let Some(member_types) = member_list(&self.member_types, descriptions) {
136 content.push(Block::Header(
137 2,
138 Attr::null(),
139 vec![Inline::Str("Member Types".to_string())],
140 ));
141
142 content.push(member_types);
143 }
144
145 if let Some(member_functions) = member_list(self.member_functions.keys(), descriptions) {
146 content.push(Block::Header(
147 2,
148 Attr::null(),
149 vec![Inline::Str("Member Functions".to_string())],
150 ));
151
152 content.push(member_functions);
153 }
154
155 if let Some(member_variables) = member_list(self.member_variables.keys(), descriptions) {
156 content.push(Block::Header(
157 2,
158 Attr::null(),
159 vec![Inline::Str("Member Variables".to_string())],
160 ));
161
162 content.push(member_variables);
163 }
164
165 content
166 }
167
168 fn leftovers(&self) -> HashMap<Usr, Entity> {
169 self.member_functions
170 .iter()
171 .map(|(usr, func)| (usr.clone(), Entity::from(func.clone())))
172 .chain(
173 self.member_variables
174 .iter()
175 .map(|(usr, var)| (usr.clone(), Entity::from(var.clone()))),
176 )
177 .collect()
178 }
179}
180
181impl PandocDisplay for Variable {}
182
183impl PandocDisplay for Function {}
184
185fn str_block(content: String) -> Block {
186 Block::Plain(vec![Inline::Str(content)])
187}
188
189fn entity_link(usr: &Usr, name: String) -> Inline {
190 use pandoc_types::definition::Target;
191 use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
192
193 use std::iter::once;
194
195 // https://url.spec.whatwg.org/#fragment-percent-encode-set
196 const FRAGMENT: &AsciiSet = &CONTROLS
197 .add(b' ')
198 .add(b'"')
199 .add(b'<')
200 .add(b'>')
201 .add(b'`')
202 .add(b'#')
203 .add(b'?')
204 .add(b'{')
205 .add(b'}');
206
207 Inline::Link(
208 Attr::null(),
209 vec![Inline::Code(Attr::null(), name)],
210 Target(
211 once("./")
212 .chain(utf8_percent_encode(&usr.0, FRAGMENT))
213 .collect(),
214 String::new(),
215 ),
216 )
217}
218
219fn raw_markdown(text: String) -> Block {
220 use pandoc_types::definition::Format;
221 Block::RawBlock(Format(String::from("markdown")), text)
222}
223
224fn member_list<'a>(
225 members: impl IntoIterator<Item = &'a Usr>,
226 descriptions: &HashMap<Usr, Description>,
227) -> Option<Block> {
228 let definitions: Vec<(Vec<Inline>, Vec<Vec<Block>>)> = members
229 .into_iter()
230 .filter_map(|usr| {
231 let name = &descriptions.get(usr)?.name;
232 Some((
233 vec![entity_link(usr, name.clone())],
234 vec![vec![str_block(
235 descriptions.get(usr).unwrap().brief.clone(),
236 )]],
237 ))
238 })
239 .collect();
240
241 if definitions.is_empty() {
242 None
243 } else {
244 Some(Block::DefinitionList(definitions))
245 }
246}