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/doxygen.rs | |
download | poseidoc-3301430c676e4af6b95d96b6408a66f9d2768653.tar.gz poseidoc-3301430c676e4af6b95d96b6408a66f9d2768653.zip |
First version
Diffstat (limited to 'src/doxygen.rs')
-rw-r--r-- | src/doxygen.rs | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/src/doxygen.rs b/src/doxygen.rs new file mode 100644 index 0000000..8d2267d --- /dev/null +++ b/src/doxygen.rs | |||
@@ -0,0 +1,454 @@ | |||
1 | /// A 'recursive descent' parser for Doxygen XML files | ||
2 | mod builders; | ||
3 | |||
4 | use xml::{ | ||
5 | attribute::OwnedAttribute, | ||
6 | reader::{EventReader, ParserConfig, XmlEvent}, | ||
7 | }; | ||
8 | |||
9 | use std::collections::HashMap; | ||
10 | use std::io::Read; | ||
11 | |||
12 | // === Types === | ||
13 | |||
14 | #[derive(Debug, Clone)] | ||
15 | pub(crate) enum DoxygenType { | ||
16 | NameSpace(NameSpace), | ||
17 | Class(Class), | ||
18 | Unhandled, | ||
19 | } | ||
20 | |||
21 | #[derive(Debug, Clone)] | ||
22 | pub(crate) struct NameSpace { | ||
23 | pub(crate) name: String, | ||
24 | pub(crate) members: Vec<String>, | ||
25 | pub(crate) brief_description: Description, | ||
26 | pub(crate) detailed_description: Description, | ||
27 | } | ||
28 | |||
29 | #[derive(Debug, Clone)] | ||
30 | pub(crate) struct FunctionArgument { | ||
31 | pub(crate) name: String, | ||
32 | pub(crate) r#type: String, | ||
33 | pub(crate) default_value: Option<String>, | ||
34 | } | ||
35 | |||
36 | #[derive(Debug, Clone)] | ||
37 | pub(crate) enum Member { | ||
38 | Variable { | ||
39 | name: String, | ||
40 | r#type: String, | ||
41 | }, | ||
42 | // What | ||
43 | /* | ||
44 | Enum { | ||
45 | |||
46 | } | ||
47 | */ | ||
48 | Function { | ||
49 | name: String, | ||
50 | args: Vec<FunctionArgument>, | ||
51 | return_type: String, | ||
52 | }, | ||
53 | Unhandled, | ||
54 | } | ||
55 | |||
56 | #[derive(Debug, Clone)] | ||
57 | pub(crate) struct Class { | ||
58 | pub(crate) name: String, | ||
59 | pub(crate) inners: Vec<(String, String)>, | ||
60 | pub(crate) sections: HashMap<String, Vec<Member>>, | ||
61 | pub(crate) brief_description: Description, | ||
62 | pub(crate) detailed_description: Description, | ||
63 | } | ||
64 | |||
65 | // === Description === | ||
66 | |||
67 | #[derive(Debug, Clone)] | ||
68 | pub(crate) struct Description { | ||
69 | pub(crate) inner: Vec<DescriptionNode>, | ||
70 | } | ||
71 | |||
72 | impl Description { | ||
73 | fn new() -> Self { | ||
74 | Self { inner: Vec::new() } | ||
75 | } | ||
76 | } | ||
77 | |||
78 | #[derive(Debug, Clone)] | ||
79 | pub(crate) enum DescriptionNode { | ||
80 | Text(String), | ||
81 | Title(String), | ||
82 | Para(Paragraph), | ||
83 | Sect1(String), | ||
84 | Internal(String), | ||
85 | } | ||
86 | |||
87 | #[derive(Debug, Clone)] | ||
88 | pub(crate) struct Paragraph { | ||
89 | pub(crate) inner: Vec<ParagraphNode>, | ||
90 | } | ||
91 | |||
92 | #[derive(Debug, Clone)] | ||
93 | pub(crate) enum ParagraphNode { | ||
94 | Text(String), | ||
95 | Ref(Ref), | ||
96 | } | ||
97 | |||
98 | #[derive(Debug, Clone)] | ||
99 | pub(crate) struct Ref { | ||
100 | pub(crate) id: String, | ||
101 | pub(crate) kind: RefKind, | ||
102 | pub(crate) text: String, | ||
103 | } | ||
104 | |||
105 | #[derive(Debug, Clone)] | ||
106 | pub(crate) enum RefKind { | ||
107 | Compound, | ||
108 | Member, | ||
109 | } | ||
110 | |||
111 | #[derive(Debug, Clone)] | ||
112 | enum DoxygenTypeKind { | ||
113 | NameSpace, | ||
114 | Unhandled, | ||
115 | } | ||
116 | |||
117 | trait TypeBuilder { | ||
118 | //const KIND: DoxygenTypeKind; | ||
119 | |||
120 | fn build(self: Box<Self>) -> DoxygenType; | ||
121 | |||
122 | fn name(&mut self, value: String); | ||
123 | fn brief_description(&mut self, description: Description); | ||
124 | fn detailed_description(&mut self, description: Description); | ||
125 | |||
126 | fn add_inner(&mut self, name: String, kind: String); | ||
127 | fn add_member(&mut self, section: String, member: Member); | ||
128 | } | ||
129 | |||
130 | trait FromXML { | ||
131 | fn from_xml(attributes: Vec<OwnedAttribute>, event_reader: &mut EventReader<impl Read>) | ||
132 | -> Self; | ||
133 | } | ||
134 | |||
135 | impl FromXML for Description { | ||
136 | fn from_xml( | ||
137 | _attributes: Vec<OwnedAttribute>, | ||
138 | mut event_reader: &mut EventReader<impl Read>, | ||
139 | ) -> Self { | ||
140 | let mut inner = Vec::new(); | ||
141 | |||
142 | while let Ok(event) = event_reader.next() { | ||
143 | match event { | ||
144 | XmlEvent::Characters(text) => inner.push(DescriptionNode::Text(text)), | ||
145 | XmlEvent::StartElement { | ||
146 | name, attributes, .. | ||
147 | } => inner.push(match name.local_name.as_str() { | ||
148 | "para" => { | ||
149 | DescriptionNode::Para(Paragraph::from_xml(attributes, &mut event_reader)) | ||
150 | } | ||
151 | "title" => DescriptionNode::Title(event_simple(&mut event_reader)), | ||
152 | "sect1" => DescriptionNode::Sect1(event_simple(&mut event_reader)), | ||
153 | "internal" => DescriptionNode::Internal(event_simple(&mut event_reader)), | ||
154 | other => { | ||
155 | warn!("Description element not supported: {}", other); | ||
156 | continue; | ||
157 | } | ||
158 | }), | ||
159 | XmlEvent::EndElement { .. } => break, | ||
160 | other => { | ||
161 | warn!("Description event not supported: {:?}", other); | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | |||
166 | Self { inner } | ||
167 | } | ||
168 | } | ||
169 | |||
170 | impl FromXML for Paragraph { | ||
171 | fn from_xml( | ||
172 | _attributes: Vec<OwnedAttribute>, | ||
173 | mut event_reader: &mut EventReader<impl Read>, | ||
174 | ) -> Self { | ||
175 | let mut inner = Vec::new(); | ||
176 | |||
177 | while let Ok(event) = event_reader.next() { | ||
178 | match event { | ||
179 | XmlEvent::Characters(text) => inner.push(ParagraphNode::Text(text)), | ||
180 | XmlEvent::StartElement { | ||
181 | name, attributes, .. | ||
182 | } => inner.push(match name.local_name.as_str() { | ||
183 | "ref" => ParagraphNode::Ref(Ref::from_xml(attributes, &mut event_reader)), | ||
184 | other => { | ||
185 | warn!("Paragraph element not supported: {}", other); | ||
186 | continue; | ||
187 | } | ||
188 | }), | ||
189 | XmlEvent::EndElement { .. } => break, | ||
190 | other => { | ||
191 | warn!("Paragraph event not supported: {:?}", other); | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | |||
196 | Self { inner } | ||
197 | } | ||
198 | } | ||
199 | |||
200 | impl FromXML for Ref { | ||
201 | fn from_xml( | ||
202 | attributes: Vec<OwnedAttribute>, | ||
203 | mut event_reader: &mut EventReader<impl Read>, | ||
204 | ) -> Self { | ||
205 | let mut id = None; | ||
206 | let mut kind = None; | ||
207 | |||
208 | for attr in attributes.into_iter() { | ||
209 | match attr.name.local_name.as_str() { | ||
210 | "refid" => id = Some(attr.value), | ||
211 | "kindref" => { | ||
212 | kind = Some(match attr.value.as_str() { | ||
213 | "compound" => RefKind::Compound, | ||
214 | "member" => RefKind::Member, | ||
215 | other => { | ||
216 | warn!("Ref kind not supported: {}", other); | ||
217 | RefKind::Compound | ||
218 | } | ||
219 | }); | ||
220 | } | ||
221 | other => { | ||
222 | warn!("Ref element not supported: {}", other); | ||
223 | } | ||
224 | } | ||
225 | } | ||
226 | |||
227 | let text = event_simple(&mut event_reader); | ||
228 | |||
229 | Ref { | ||
230 | id: id.unwrap(), | ||
231 | kind: kind.unwrap(), | ||
232 | text, | ||
233 | } | ||
234 | } | ||
235 | } | ||
236 | |||
237 | /// Parse a type from a XML Doxygen file | ||
238 | pub(crate) fn parse_type(reader: impl Read) -> Vec<DoxygenType> { | ||
239 | let mut event_reader = EventReader::new(reader); | ||
240 | |||
241 | match event_reader.next().unwrap() { | ||
242 | XmlEvent::StartDocument { .. } => (), | ||
243 | _ => panic!("XML file does not begin with document"), | ||
244 | } | ||
245 | |||
246 | match event_reader.next().unwrap() { | ||
247 | XmlEvent::StartElement { name, .. } => { | ||
248 | if name.local_name != "doxygen" { | ||
249 | panic!("XML file does not start with a 'doxygen' element"); | ||
250 | } | ||
251 | } | ||
252 | _ => panic!("XML file does not start with a 'doxygen' element"), | ||
253 | } | ||
254 | |||
255 | let mut result = Vec::new(); | ||
256 | |||
257 | while let Ok(event) = event_reader.next() { | ||
258 | match dbg!(event) { | ||
259 | XmlEvent::StartElement { | ||
260 | name, attributes, .. | ||
261 | } => { | ||
262 | if name.local_name != "compounddef" { | ||
263 | panic!("'doxygen' is not a sequence of 'compounddef' elements"); | ||
264 | } | ||
265 | |||
266 | result.push(event_compounddef(attributes, &mut event_reader)); | ||
267 | } | ||
268 | XmlEvent::Whitespace(_) => (), | ||
269 | XmlEvent::EndElement { .. } => break, | ||
270 | _ => panic!("'doxygen' is not a sequence of 'compounddef' elements"), | ||
271 | } | ||
272 | } | ||
273 | |||
274 | // Unnecessarily strict? | ||
275 | match event_reader.next().unwrap() { | ||
276 | XmlEvent::EndDocument => (), | ||
277 | _ => panic!("XML file does not end after 'doxygen' element"), | ||
278 | } | ||
279 | |||
280 | dbg!(result) | ||
281 | } | ||
282 | |||
283 | fn event_compounddef( | ||
284 | attributes: Vec<OwnedAttribute>, | ||
285 | mut event_reader: &mut EventReader<impl Read>, | ||
286 | ) -> DoxygenType { | ||
287 | let kind = attributes | ||
288 | .into_iter() | ||
289 | .find_map(|attr| { | ||
290 | if attr.name.local_name == "kind" { | ||
291 | Some(attr.value) | ||
292 | } else { | ||
293 | None | ||
294 | } | ||
295 | }) | ||
296 | .unwrap(); | ||
297 | |||
298 | let mut builder = builders::builder_for(kind); | ||
299 | |||
300 | while let Ok(event) = event_reader.next() { | ||
301 | match dbg!(event) { | ||
302 | XmlEvent::StartElement { | ||
303 | name, attributes, .. | ||
304 | } => match name.local_name.as_str() { | ||
305 | "compoundname" => { | ||
306 | let name = event_simple(&mut event_reader); | ||
307 | builder.name(name); | ||
308 | } | ||
309 | "briefdescription" => { | ||
310 | let brief_description = Description::from_xml(attributes, &mut event_reader); | ||
311 | builder.brief_description(brief_description); | ||
312 | } | ||
313 | "detaileddescription" => { | ||
314 | let detailed_description = Description::from_xml(attributes, &mut event_reader); | ||
315 | builder.detailed_description(detailed_description); | ||
316 | } | ||
317 | "location" => { | ||
318 | event_simple(&mut event_reader); | ||
319 | debug!("Do something?"); | ||
320 | } | ||
321 | "sectiondef" => { | ||
322 | event_section(&mut builder, &mut event_reader); | ||
323 | } | ||
324 | other_name if is_inner_type(other_name) => { | ||
325 | let inner = event_simple(&mut event_reader); | ||
326 | builder.add_inner(inner, other_name.to_string()); | ||
327 | } | ||
328 | _other_name => { | ||
329 | event_ignore(&mut event_reader); | ||
330 | } | ||
331 | }, | ||
332 | XmlEvent::Whitespace(_) => (), | ||
333 | XmlEvent::EndElement { .. } => break, | ||
334 | _ => panic!("Unhandled XML event"), | ||
335 | } | ||
336 | } | ||
337 | |||
338 | builder.build() | ||
339 | } | ||
340 | |||
341 | /// Returns true if the given XML Element is a reference to another inner type. | ||
342 | /// | ||
343 | /// Corresponds to the "refType" XSD type in `compound.xsd` | ||
344 | fn is_inner_type(element_name: &str) -> bool { | ||
345 | match element_name { | ||
346 | "innerdir" | "innerfile" | "innerclass" | "innernamespace" | "innerpage" | "innergroup" => { | ||
347 | true | ||
348 | } | ||
349 | _ => false, | ||
350 | } | ||
351 | } | ||
352 | |||
353 | /// Get the text inside a simple, non recursive XML tag | ||
354 | fn event_simple(event_reader: &mut EventReader<impl Read>) -> String { | ||
355 | let result; | ||
356 | |||
357 | match dbg!(event_reader.next().unwrap()) { | ||
358 | XmlEvent::Characters(tmp_result) => { | ||
359 | result = tmp_result; | ||
360 | } | ||
361 | XmlEvent::EndElement { .. } => return "".to_string(), | ||
362 | _ => panic!("Simple XML event is not so simple"), | ||
363 | } | ||
364 | |||
365 | match dbg!(event_reader.next().unwrap()) { | ||
366 | XmlEvent::EndElement { .. } => (), | ||
367 | _ => panic!("Simple XML event is not so simple"), | ||
368 | } | ||
369 | |||
370 | result | ||
371 | } | ||
372 | |||
373 | fn event_ignore(mut event_reader: &mut EventReader<impl Read>) { | ||
374 | loop { | ||
375 | let event = event_reader.next().unwrap(); | ||
376 | match dbg!(event) { | ||
377 | XmlEvent::StartElement { .. } => event_ignore(&mut event_reader), | ||
378 | XmlEvent::EndElement { .. } => break, | ||
379 | _ => (), | ||
380 | } | ||
381 | } | ||
382 | } | ||
383 | |||
384 | fn event_section( | ||
385 | mut builder: &mut Box<dyn TypeBuilder>, | ||
386 | mut event_reader: &mut EventReader<impl Read>, | ||
387 | ) { | ||
388 | loop { | ||
389 | let event = event_reader.next().unwrap(); | ||
390 | match dbg!(event) { | ||
391 | XmlEvent::StartElement { | ||
392 | name, attributes, .. | ||
393 | } => match name.local_name.as_str() { | ||
394 | "memberdef" => { | ||
395 | let member = event_member(attributes, &mut event_reader); | ||
396 | builder.add_member("bla".to_owned(), member); | ||
397 | } | ||
398 | _ => panic!("Unhandled thing"), | ||
399 | }, | ||
400 | |||
401 | XmlEvent::Whitespace(_) => (), | ||
402 | XmlEvent::EndElement { .. } => break, | ||
403 | _ => panic!("Unknown section XML event"), | ||
404 | } | ||
405 | } | ||
406 | } | ||
407 | |||
408 | fn event_member( | ||
409 | attributes: Vec<OwnedAttribute>, | ||
410 | mut event_reader: &mut EventReader<impl Read>, | ||
411 | ) -> Member { | ||
412 | let kind = attributes | ||
413 | .into_iter() | ||
414 | .find_map(|attr| { | ||
415 | if attr.name.local_name == "kind" { | ||
416 | Some(attr.value) | ||
417 | } else { | ||
418 | None | ||
419 | } | ||
420 | }) | ||
421 | .unwrap(); | ||
422 | |||
423 | match kind.as_str() { | ||
424 | "variable" => event_member_variable(&mut event_reader), | ||
425 | _ => { | ||
426 | event_ignore(&mut event_reader); | ||
427 | Member::Unhandled | ||
428 | } | ||
429 | } | ||
430 | } | ||
431 | |||
432 | fn event_member_variable(mut event_reader: &mut EventReader<impl Read>) -> Member { | ||
433 | let mut member_name = None; | ||
434 | let mut r#type = None; | ||
435 | |||
436 | loop { | ||
437 | let event = event_reader.next().unwrap(); | ||
438 | match dbg!(event) { | ||
439 | XmlEvent::StartElement { name, .. } => match name.local_name.as_str() { | ||
440 | "name" => member_name = Some(event_simple(&mut event_reader)), | ||
441 | "definition" => r#type = Some(event_simple(&mut event_reader)), | ||
442 | _ => event_ignore(&mut event_reader), | ||
443 | }, | ||
444 | XmlEvent::Whitespace(_) => (), | ||
445 | XmlEvent::EndElement { .. } => break, | ||
446 | _ => panic!("Unknown member XML event"), | ||
447 | } | ||
448 | } | ||
449 | |||
450 | Member::Variable { | ||
451 | name: member_name.unwrap(), | ||
452 | r#type: r#type.unwrap(), | ||
453 | } | ||
454 | } | ||