diff options
author | Minijackson <minijackson@riseup.net> | 2019-09-08 16:15:46 +0200 |
---|---|---|
committer | Minijackson <minijackson@riseup.net> | 2019-11-10 16:37:59 +0100 |
commit | 3301430c676e4af6b95d96b6408a66f9d2768653 (patch) | |
tree | 12810ce81a3b1d3cb23270fc5119016d5f6c325a /src/pandoc.rs | |
download | poseidoc-3301430c676e4af6b95d96b6408a66f9d2768653.tar.gz poseidoc-3301430c676e4af6b95d96b6408a66f9d2768653.zip |
First version
Diffstat (limited to 'src/pandoc.rs')
-rw-r--r-- | src/pandoc.rs | 246 |
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 | |||
3 | use crate::entities::*; | ||
4 | |||
5 | use pandoc_types::definition::{Attr, Block, Inline, Meta, MetaValue, Pandoc}; | ||
6 | |||
7 | use std::collections::HashMap; | ||
8 | |||
9 | impl 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 | ||
52 | trait 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 | |||
66 | impl 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 | |||
78 | impl 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 | |||
107 | impl 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 | |||
131 | impl 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 | |||
181 | impl PandocDisplay for Variable {} | ||
182 | |||
183 | impl PandocDisplay for Function {} | ||
184 | |||
185 | fn str_block(content: String) -> Block { | ||
186 | Block::Plain(vec![Inline::Str(content)]) | ||
187 | } | ||
188 | |||
189 | fn 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 | |||
219 | fn raw_markdown(text: String) -> Block { | ||
220 | use pandoc_types::definition::Format; | ||
221 | Block::RawBlock(Format(String::from("markdown")), text) | ||
222 | } | ||
223 | |||
224 | fn 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 | } | ||