summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMinijackson <minijackson@riseup.net>2019-09-08 16:15:46 +0200
committerMinijackson <minijackson@riseup.net>2019-11-10 16:37:59 +0100
commit3301430c676e4af6b95d96b6408a66f9d2768653 (patch)
tree12810ce81a3b1d3cb23270fc5119016d5f6c325a /src
downloadposeidoc-3301430c676e4af6b95d96b6408a66f9d2768653.tar.gz
poseidoc-3301430c676e4af6b95d96b6408a66f9d2768653.zip
First version
Diffstat (limited to 'src')
-rw-r--r--src/cli.rs18
-rw-r--r--src/doxygen.rs454
-rw-r--r--src/doxygen/builders.rs109
-rw-r--r--src/entities.rs134
-rw-r--r--src/generator.rs209
-rw-r--r--src/main.rs44
-rw-r--r--src/pandoc.rs246
-rw-r--r--src/pandoc/types.rs39
-rw-r--r--src/parsing.rs457
9 files changed, 1710 insertions, 0 deletions
diff --git a/src/cli.rs b/src/cli.rs
new file mode 100644
index 0000000..e106de7
--- /dev/null
+++ b/src/cli.rs
@@ -0,0 +1,18 @@
1use structopt::StructOpt;
2
3#[derive(Debug, Clone, StructOpt)]
4pub(crate) struct Cli {
5 #[structopt(long, short, parse(from_occurrences))]
6 pub(crate) verbosity: u8,
7
8 #[structopt(long, number_of_values = 1, parse(try_from_str = shell_words::split))]
9 pub(crate) extra_arg: Vec<Vec<String>>,
10
11 #[structopt(subcommand)]
12 pub(crate) command: Command,
13}
14
15#[derive(Debug, Clone, StructOpt)]
16pub(crate) enum Command {
17 Generate { file: String },
18}
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
2mod builders;
3
4use xml::{
5 attribute::OwnedAttribute,
6 reader::{EventReader, ParserConfig, XmlEvent},
7};
8
9use std::collections::HashMap;
10use std::io::Read;
11
12// === Types ===
13
14#[derive(Debug, Clone)]
15pub(crate) enum DoxygenType {
16 NameSpace(NameSpace),
17 Class(Class),
18 Unhandled,
19}
20
21#[derive(Debug, Clone)]
22pub(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)]
30pub(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)]
37pub(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)]
57pub(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)]
68pub(crate) struct Description {
69 pub(crate) inner: Vec<DescriptionNode>,
70}
71
72impl Description {
73 fn new() -> Self {
74 Self { inner: Vec::new() }
75 }
76}
77
78#[derive(Debug, Clone)]
79pub(crate) enum DescriptionNode {
80 Text(String),
81 Title(String),
82 Para(Paragraph),
83 Sect1(String),
84 Internal(String),
85}
86
87#[derive(Debug, Clone)]
88pub(crate) struct Paragraph {
89 pub(crate) inner: Vec<ParagraphNode>,
90}
91
92#[derive(Debug, Clone)]
93pub(crate) enum ParagraphNode {
94 Text(String),
95 Ref(Ref),
96}
97
98#[derive(Debug, Clone)]
99pub(crate) struct Ref {
100 pub(crate) id: String,
101 pub(crate) kind: RefKind,
102 pub(crate) text: String,
103}
104
105#[derive(Debug, Clone)]
106pub(crate) enum RefKind {
107 Compound,
108 Member,
109}
110
111#[derive(Debug, Clone)]
112enum DoxygenTypeKind {
113 NameSpace,
114 Unhandled,
115}
116
117trait 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
130trait FromXML {
131 fn from_xml(attributes: Vec<OwnedAttribute>, event_reader: &mut EventReader<impl Read>)
132 -> Self;
133}
134
135impl 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
170impl 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
200impl 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
238pub(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
283fn 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`
344fn 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
354fn 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
373fn 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
384fn 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
408fn 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
432fn 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}
diff --git a/src/doxygen/builders.rs b/src/doxygen/builders.rs
new file mode 100644
index 0000000..9b60325
--- /dev/null
+++ b/src/doxygen/builders.rs
@@ -0,0 +1,109 @@
1use super::*;
2
3pub(super) fn builder_for(doxygen_kind: String) -> Box<dyn TypeBuilder> {
4 match doxygen_kind.as_str() {
5 "namespace" => Box::new(NameSpaceBuilder::default()),
6 "class" | "struct" => Box::new(ClassBuilder::default()),
7 _ => Box::new(UnhandledBuilder),
8 }
9}
10
11#[derive(Debug, Default)]
12struct NameSpaceBuilder {
13 name: Option<String>,
14 members: Vec<String>,
15 brief_description: Option<Description>,
16 detailed_description: Option<Description>,
17}
18
19impl TypeBuilder for NameSpaceBuilder {
20 fn build(self: Box<Self>) -> DoxygenType {
21 let name = self.name.unwrap();
22
23 DoxygenType::NameSpace(NameSpace {
24 name,
25 members: self.members,
26 brief_description: self.brief_description.unwrap(),
27 detailed_description: self.detailed_description.unwrap(),
28 })
29 }
30
31 fn name(&mut self, value: String) {
32 self.name = Some(value);
33 }
34
35 fn add_inner(&mut self, name: String, kind: String) {
36 self.members.push(name);
37 }
38
39 fn add_member(&mut self, _section: String, _member: Member) {
40 panic!("Adding member in namespace?");
41 }
42
43 fn brief_description(&mut self, description: Description) {
44 self.brief_description = Some(description);
45 }
46
47 fn detailed_description(&mut self, description: Description) {
48 self.brief_description = Some(description);
49 }
50}
51
52#[derive(Debug, Default)]
53struct UnhandledBuilder;
54
55impl TypeBuilder for UnhandledBuilder {
56 fn build(self: Box<Self>) -> DoxygenType {
57 DoxygenType::Unhandled
58 }
59
60 fn name(&mut self, _value: String) {}
61
62 fn add_inner(&mut self, _name: String, _kind: String) {}
63 fn add_member(&mut self, _section: String, _member: Member) {}
64 fn brief_description(&mut self, _description: Description) {}
65 fn detailed_description(&mut self, _description: Description) {}
66}
67
68#[derive(Debug, Default)]
69struct ClassBuilder {
70 name: Option<String>,
71 inners: Vec<(String, String)>,
72 sections: HashMap<String, Vec<Member>>,
73 brief_description: Option<Description>,
74 detailed_description: Option<Description>,
75}
76
77impl TypeBuilder for ClassBuilder {
78 fn build(self: Box<Self>) -> DoxygenType {
79 let name = self.name.unwrap();
80
81 DoxygenType::Class(Class {
82 name,
83 inners: self.inners,
84 sections: self.sections,
85 brief_description: self.brief_description.unwrap_or(Description::new()),
86 detailed_description: self.detailed_description.unwrap_or(Description::new()),
87 })
88 }
89
90 fn name(&mut self, value: String) {
91 self.name = Some(value);
92 }
93
94 fn add_inner(&mut self, name: String, kind: String) {
95 self.inners.push((name, kind));
96 }
97
98 fn add_member(&mut self, section: String, member: Member) {
99 self.sections.entry(section).or_default().push(member);
100 }
101
102 fn brief_description(&mut self, description: Description) {
103 self.brief_description = Some(description);
104 }
105
106 fn detailed_description(&mut self, description: Description) {
107 self.brief_description = Some(description);
108 }
109}
diff --git a/src/entities.rs b/src/entities.rs
new file mode 100644
index 0000000..b7368df
--- /dev/null
+++ b/src/entities.rs
@@ -0,0 +1,134 @@
1use std::collections::HashMap;
2
3#[derive(Debug, Clone, Hash, PartialEq, Eq)]
4pub(crate) struct Usr(pub(crate) String);
5
6#[derive(Debug, Clone)]
7pub(crate) struct EntitiesManager {
8 toplevel_entities: HashMap<Usr, Entity>,
9 descriptions: HashMap<Usr, Description>,
10}
11
12pub(crate) struct EntitiesManagerComponents {
13 pub(crate) toplevel_entities: HashMap<Usr, Entity>,
14 pub(crate) descriptions: HashMap<Usr, Description>,
15}
16
17impl EntitiesManager {
18 pub fn new() -> Self {
19 EntitiesManager {
20 toplevel_entities: HashMap::new(),
21 descriptions: HashMap::new(),
22 }
23 }
24
25 pub fn insert(&mut self, usr: Usr, description: Description) {
26 info!("Found entity {:?}", description.name);
27 self.descriptions.insert(usr, description);
28 }
29
30 pub fn insert_toplevel(&mut self, usr: Usr, entity: Entity) {
31 self.toplevel_entities.insert(usr, entity);
32 }
33
34 pub fn decompose(self) -> EntitiesManagerComponents {
35 EntitiesManagerComponents {
36 toplevel_entities: self.toplevel_entities,
37 descriptions: self.descriptions,
38 }
39 }
40}
41
42#[derive(Debug, Clone, Default)]
43pub(crate) struct Description {
44 pub(crate) name: String,
45 pub(crate) brief: String,
46 pub(crate) detailed: String,
47}
48
49impl Description {
50 pub(crate) fn with_name(name: String) -> Self {
51 Description {
52 name,
53 ..Default::default()
54 }
55 }
56}
57
58#[derive(Debug, Clone)]
59pub(crate) struct Described<T> {
60 pub(crate) description: Description,
61 pub(crate) entity: T,
62}
63
64#[derive(Debug, Clone)]
65pub(crate) enum Entity {
66 NameSpace(NameSpace),
67 Variable(Variable),
68 Function(Function),
69 Class(Class),
70}
71
72impl Entity {
73 fn kind_name(&self) -> &'static str {
74 match self {
75 Entity::NameSpace(_) => "namespace",
76 Entity::Variable(_) => "variable",
77 Entity::Function(_) => "function",
78 Entity::Class(_) => "class",
79 }
80 }
81}
82
83#[derive(Debug, Clone)]
84pub(crate) struct NameSpace {
85 pub(crate) members: HashMap<Usr, Entity>,
86}
87
88impl From<NameSpace> for Entity {
89 fn from(ns: NameSpace) -> Self {
90 Entity::NameSpace(ns)
91 }
92}
93
94#[derive(Debug, Clone)]
95pub(crate) struct Variable {
96 pub(crate) r#type: String,
97}
98
99impl From<Variable> for Entity {
100 fn from(var: Variable) -> Self {
101 Entity::Variable(var)
102 }
103}
104
105#[derive(Debug, Clone)]
106pub(crate) struct Function {
107 pub(crate) arguments: Vec<FunctionArgument>,
108 pub(crate) return_type: String,
109}
110
111impl From<Function> for Entity {
112 fn from(func: Function) -> Self {
113 Entity::Function(func)
114 }
115}
116
117#[derive(Debug, Clone)]
118pub(crate) struct FunctionArgument {
119 pub(crate) name: String,
120 pub(crate) r#type: String,
121}
122
123#[derive(Debug, Clone)]
124pub(crate) struct Class {
125 pub(crate) member_types: Vec<Usr>,
126 pub(crate) member_functions: HashMap<Usr, Function>,
127 pub(crate) member_variables: HashMap<Usr, Variable>,
128}
129
130impl From<Class> for Entity {
131 fn from(class: Class) -> Self {
132 Entity::Class(class)
133 }
134}
diff --git a/src/generator.rs b/src/generator.rs
new file mode 100644
index 0000000..0ab4511
--- /dev/null
+++ b/src/generator.rs
@@ -0,0 +1,209 @@
1use crate::entities::{
2 Described, Description, EntitiesManager, EntitiesManagerComponents, Entity, Usr,
3};
4
5use anyhow::{Context, Result};
6use thiserror::Error;
7use threadpool::ThreadPool;
8
9use std::collections::HashMap;
10use std::path::Path;
11use std::sync::{mpsc::channel, Arc};
12
13pub(crate) fn generate(base_dir: &Path, manager: EntitiesManager) -> Result<()> {
14 let EntitiesManagerComponents {
15 toplevel_entities,
16 descriptions,
17 } = manager.decompose();
18
19 let md_output_dir = base_dir.join("markdown");
20 let html_output_dir = base_dir.join("html");
21
22 std::fs::create_dir_all(&md_output_dir)
23 .context("Failed to create the markdown output directory")?;
24 std::fs::create_dir_all(&html_output_dir)
25 .context("Failed to create the HTML output directory")?;
26
27 let pool = ThreadPool::new(num_cpus::get());
28
29 let descriptions = Arc::new(descriptions);
30
31 let (tx, rx) = channel::<()>();
32
33 for (usr, entity) in toplevel_entities {
34 generate_recursively(
35 usr,
36 entity,
37 pool.clone(),
38 tx.clone(),
39 descriptions.clone(),
40 &md_output_dir,
41 &html_output_dir,
42 );
43 }
44
45 drop(tx);
46
47 // This is not really idiomatic, but iter returns None when every Sender is destroyed, so just
48 // by passing around Senders in generate_recursively, we wait for every job.
49 rx.iter().for_each(drop);
50
51 Ok(())
52}
53
54fn generate_recursively(
55 usr: Usr,
56 entity: Entity,
57 pool: ThreadPool,
58 tx: std::sync::mpsc::Sender<()>,
59 descriptions: Arc<HashMap<Usr, Description>>,
60 md_output_dir: impl AsRef<Path>,
61 html_output_dir: impl AsRef<Path>,
62) {
63 let descriptions = descriptions.clone();
64 let md_output_dir = md_output_dir.as_ref().to_owned();
65 let html_output_dir = html_output_dir.as_ref().to_owned();
66
67 let pool2 = pool.clone();
68 pool.execute(move || {
69 trace!("Trying to generate {}", usr.0);
70
71 let entity = Described::<Entity> {
72 entity,
73 description: descriptions.get(&usr).unwrap().clone(),
74 };
75
76 let leftovers = generate_single(
77 &usr,
78 entity,
79 &descriptions,
80 &md_output_dir,
81 &html_output_dir,
82 )
83 .unwrap();
84
85 for (usr, entity) in leftovers {
86 let pool = pool2.clone();
87 let tx = tx.clone();
88 generate_recursively(
89 usr,
90 entity,
91 pool,
92 tx,
93 descriptions.clone(),
94 md_output_dir.clone(),
95 html_output_dir.clone(),
96 );
97 }
98 });
99}
100
101fn generate_single(
102 usr: &Usr,
103 entity: Described<Entity>,
104 descriptions: &HashMap<Usr, Description>,
105 md_output_dir: impl AsRef<Path>,
106 html_output_dir: impl AsRef<Path>,
107) -> Result<HashMap<Usr, Entity>> {
108 use std::io::prelude::*;
109 use std::process::{Command, Stdio};
110
111 let md_output_file = md_output_dir.as_ref().join(&usr.0);
112 let html_output_file = html_output_dir.as_ref().join(&usr.0);
113
114 let mut command = Command::new("pandoc");
115
116 command
117 .stdin(Stdio::piped())
118 .stdout(Stdio::piped())
119 .stderr(Stdio::piped())
120 .args(&[
121 "--from=json",
122 "--to=markdown",
123 "--output",
124 md_output_file
125 .to_str()
126 .context("Entity name is not valid UTF-8")?,
127 ]);
128
129 debug!("Launching command: {:?}", command);
130
131 let mut pandoc = command
132 .spawn()
133 .context("Failed to execute Pandoc command")?;
134
135 let (pandoc_ast, leftovers) = entity.into_pandoc(&descriptions);
136
137 if log_enabled!(log::Level::Trace) {
138 let json =
139 serde_json::to_string(&pandoc_ast).context("Failed to convert Pandoc AST to JSON")?;
140 trace!("Sending json: {}", json);
141 write!(
142 pandoc.stdin.as_mut().expect("Pandoc stdin not captured"),
143 "{}",
144 &json,
145 )
146 .context("Failed to convert Pandoc AST to JSON")?;
147 } else {
148 serde_json::to_writer(
149 pandoc.stdin.as_mut().expect("Pandoc stdin not captured"),
150 &pandoc_ast,
151 )
152 .context("Failed to convert Pandoc AST to JSON")?;
153 }
154
155 let output = pandoc
156 .wait_with_output()
157 .expect("Pandoc command wasn't running");
158
159 if !output.status.success() {
160 Err(CommandError {
161 status: output.status,
162 stderr: String::from_utf8(output.stderr).expect("Pandoc outputted invalid UTF-8"),
163 })
164 .context("Pandoc command failed")?;
165 }
166
167 let mut command = Command::new("pandoc");
168 command
169 .stdin(Stdio::piped())
170 .stdout(Stdio::piped())
171 .stderr(Stdio::piped())
172 .args(&[
173 "--from=markdown",
174 "--to=html",
175 "--css=res/style.css",
176 "--standalone",
177 "--self-contained",
178 md_output_file
179 .to_str()
180 .context("Entity name is not valid UTF-8")?,
181 "--output",
182 html_output_file
183 .to_str()
184 .context("Entity name is not valid UTF-8")?,
185 ]);
186
187 debug!("Launching command: {:?}", command);
188
189 let output = command
190 .output()
191 .context("Failed to execute Pandoc command")?;
192
193 if !output.status.success() {
194 Err(CommandError {
195 status: output.status,
196 stderr: String::from_utf8(output.stderr).expect("Pandoc outputted invalid UTF-8"),
197 })
198 .context("Pandoc command failed")?;
199 }
200
201 Ok(leftovers)
202}
203
204#[derive(Debug, Clone, Error)]
205#[error("Command returned status {:?} and stderr {:?}", status, stderr)]
206struct CommandError {
207 status: std::process::ExitStatus,
208 stderr: String,
209}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..e1d4752
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,44 @@
1//mod doxygen;
2mod cli;
3mod entities;
4mod generator;
5mod pandoc;
6mod parsing;
7
8#[macro_use]
9extern crate log;
10
11use cli::Command;
12use generator::generate;
13use parsing::parse_file;
14
15use anyhow::Result;
16use structopt::StructOpt;
17
18fn main() -> Result<()> {
19 let cli = cli::Cli::from_args();
20 pretty_env_logger::formatted_builder()
21 .filter(
22 None,
23 match cli.verbosity {
24 // Warnings and errors for internal warnings / errors
25 0 => log::LevelFilter::Warn,
26 1 => log::LevelFilter::Info,
27 2 => log::LevelFilter::Debug,
28 _ => log::LevelFilter::Trace,
29 },
30 )
31 .try_init()?;
32
33 match cli.command {
34 Command::Generate { file } => {
35 let extra_args = cli.extra_arg.iter().flatten().map(AsRef::as_ref).collect();
36 let manager = parse_file(file, extra_args);
37
38 let base_output_dir = std::path::Path::new("doc");
39 generate(&base_output_dir, manager)?;
40 }
41 }
42
43 Ok(())
44}
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}
diff --git a/src/pandoc/types.rs b/src/pandoc/types.rs
new file mode 100644
index 0000000..dc5be64
--- /dev/null
+++ b/src/pandoc/types.rs
@@ -0,0 +1,39 @@
1use crate::pandoc::{Block, Inline};
2
3#[derive(Debug, Clone)]
4pub(super) struct Class {
5 inners: Vec<Inner>,
6}
7
8#[derive(Debug, Clone)]
9struct Inner {
10 kind: InnerKind,
11 name: String,
12 //refid: String
13}
14
15#[derive(Debug, Clone)]
16enum InnerKind {
17 Class,
18 Enum,
19}
20
21impl std::fmt::Display for InnerKind {
22 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
23 match self {
24 InnerKind::Class => write!(f, "class"),
25 InnerKind::Enum => write!(f, "enum"),
26 }
27 }
28}
29
30impl From<Inner> for (Vec<Inline>, Vec<Vec<Block>>) {
31 fn from(inner: Inner) -> (Vec<Inline>, Vec<Vec<Block>>) {
32 (
33 vec![Inline::Str(inner.name)],
34 vec![vec![Block::Plain(vec![Inline::Str(
35 inner.kind.to_string(),
36 )])]],
37 )
38 }
39}
diff --git a/src/parsing.rs b/src/parsing.rs
new file mode 100644
index 0000000..137b89d
--- /dev/null
+++ b/src/parsing.rs
@@ -0,0 +1,457 @@
1use crate::entities::*;
2
3use clang::{Clang, Index};
4use codemap::CodeMap;
5
6use std::collections::HashMap;
7use std::path::{Path, PathBuf};
8
9pub(crate) fn parse_file<T>(path: T, mut extra_args: Vec<&str>) -> EntitiesManager
10where
11 T: Into<PathBuf>,
12 T: AsRef<Path>,
13 T: ToString,
14{
15 let mut codemap = CodeMap::new();
16 let file_map = codemap.add_file(path.to_string(), std::fs::read_to_string(&path).unwrap());
17 let file_span = file_map.span;
18
19 let clang = Clang::new().unwrap();
20 let index = Index::new(&clang, true, false);
21 let mut parser = index.parser(path);
22 parser.skip_function_bodies(true);
23
24 extra_args.push("-Werror=documentation");
25 parser.arguments(&extra_args);
26
27 if log_enabled!(log::Level::Debug) {
28 for extra_arg in extra_args {
29 debug!("Extra LibClang argument: {:?}", extra_arg);
30 }
31 }
32
33 let trans_unit = parser.parse().unwrap();
34
35 let mut manager = EntitiesManager::new();
36
37 trans_unit.get_entity().visit_children(|entity, _parent| {
38 if entity.is_in_system_header() {
39 trace!(
40 "Entity is in system header, skipping: USR = {:?}",
41 entity.get_display_name()
42 );
43 return clang::EntityVisitResult::Continue;
44 }
45
46 // TODO: wrap this callback in another function so that we can use the
47 // "?" operator instead of all these `match`es
48 let usr = match entity.get_usr() {
49 Some(usr) => usr,
50 None => return clang::EntityVisitResult::Continue,
51 };
52 trace!("Entity with USR = {:?}", usr);
53
54 let name = match entity.get_name() {
55 Some(name) => name,
56 None => return clang::EntityVisitResult::Continue,
57 };
58
59 debug!("Parsing toplevel entity with name = {:?}", name);
60
61 let entity = entity.into_poseidoc_entity(&mut manager);
62
63 manager.insert_toplevel(usr.into(), entity);
64
65 clang::EntityVisitResult::Continue
66 });
67
68 use codemap_diagnostic::{ColorConfig, Emitter};
69
70 let mut emitter = Emitter::stderr(ColorConfig::Auto, Some(&codemap));
71
72 for diagnostic in trans_unit.get_diagnostics().iter() {
73 let main_diag = match clang_diag_to_codemap_diag(&diagnostic, &file_span) {
74 Some(diag) => diag,
75 None => continue,
76 };
77
78 let sub_diags = diagnostic
79 .get_children()
80 .into_iter()
81 .filter_map(|diagnostic| clang_diag_to_codemap_diag(&diagnostic, &file_span));
82
83 let fix_it_diags = diagnostic
84 .get_fix_its()
85 .into_iter()
86 .map(|fix_it| clang_fix_it_to_codemap_diag(&fix_it, &file_span));
87
88 emitter.emit(
89 &std::iter::once(main_diag)
90 .chain(sub_diags)
91 .chain(fix_it_diags)
92 .collect::<Vec<_>>(),
93 );
94 }
95
96 manager
97}
98
99fn clang_diag_to_codemap_diag(
100 diagnostic: &clang::diagnostic::Diagnostic,
101 file_span: &codemap::Span,
102) -> Option<codemap_diagnostic::Diagnostic> {
103 use codemap_diagnostic::{Diagnostic, Level, SpanLabel, SpanStyle};
104
105 let ranges = diagnostic.get_ranges();
106
107 let (begin, end) = if ranges.is_empty() {
108 let location = diagnostic.get_location();
109
110 (
111 location.get_file_location().offset as u64,
112 location.get_file_location().offset as u64,
113 )
114 } else {
115 let range = diagnostic.get_ranges()[0];
116 if !range.is_in_main_file() {
117 warn!("Skipping diagnostic: {:?}", diagnostic);
118 return None;
119 }
120 (
121 range.get_start().get_file_location().offset as u64,
122 range.get_end().get_file_location().offset as u64,
123 )
124 };
125
126 let label = SpanLabel {
127 span: file_span.subspan(begin, end),
128 label: None,
129 style: SpanStyle::Primary,
130 };
131
132 Some(Diagnostic {
133 level: match diagnostic.get_severity() {
134 clang::diagnostic::Severity::Ignored => return None,
135 clang::diagnostic::Severity::Note => Level::Note,
136 clang::diagnostic::Severity::Warning => Level::Warning,
137 clang::diagnostic::Severity::Error => Level::Error,
138 clang::diagnostic::Severity::Fatal => Level::Error,
139 },
140 message: diagnostic.get_text(),
141 //code: Some("-Werror=documentation".to_string()),
142 code: None,
143 spans: vec![label],
144 })
145}
146
147fn clang_fix_it_to_codemap_diag(
148 fix_it: &clang::diagnostic::FixIt,
149 file_span: &codemap::Span,
150) -> codemap_diagnostic::Diagnostic {
151 use clang::diagnostic::FixIt;
152 use codemap_diagnostic::{Diagnostic, Level, SpanLabel, SpanStyle};
153
154 let label = match fix_it {
155 FixIt::Deletion(range) => {
156 let begin = range.get_start().get_file_location().offset as u64;
157 let end = range.get_end().get_file_location().offset as u64;
158
159 SpanLabel {
160 span: file_span.subspan(begin, end),
161 label: Some(String::from("Try deleting this")),
162 style: SpanStyle::Primary,
163 }
164 }
165 FixIt::Insertion(range, text) => {
166 let location = range.get_file_location().offset as u64;
167
168 SpanLabel {
169 span: file_span.subspan(location, location),
170 label: Some(format!("Try insterting {:?}", text)),
171 style: SpanStyle::Primary,
172 }
173 }
174 FixIt::Replacement(range, text) => {
175 let begin = range.get_start().get_file_location().offset as u64;
176 let end = range.get_end().get_file_location().offset as u64;
177
178 SpanLabel {
179 span: file_span.subspan(begin, end),
180 label: Some(format!("Try replacing this with {:?}", text)),
181 style: SpanStyle::Primary,
182 }
183 }
184 };
185
186 Diagnostic {
187 level: Level::Help,
188 message: String::from("Possible fix"),
189 //code: Some("-Werror=documentation".to_string()),
190 code: None,
191 spans: vec![label],
192 }
193}
194
195pub(crate) struct Comment(String);
196
197impl Comment {
198 pub fn from_raw(raw: String) -> Self {
199 #[derive(Debug)]
200 enum CommentStyle {
201 // Comments of type `/**` or `/*!`
202 Starred,
203 // Comments of type `///`
204 SingleLine,
205 }
206
207 let mut chars = raw.chars();
208 let style = match &chars.as_str()[..3] {
209 "/*!" | "/**" => CommentStyle::Starred,
210 "///" => CommentStyle::SingleLine,
211 _ => panic!("Comment is empty or doesn't start with either `///`, `/**`, or `/*!`"),
212 };
213
214 chars.nth(2);
215
216 let mut result = String::new();
217
218 'parse_loop: loop {
219 let maybe_space = chars.next();
220 let mut empty_line = false;
221 match maybe_space {
222 // TODO: Warn on empty comments
223 None => break,
224 Some(' ') => {}
225 Some('\n') => {
226 empty_line = true;
227 result.push('\n');
228 }
229 Some(ch) => result.push(ch),
230 }
231
232 if !empty_line {
233 let rest = chars.as_str();
234 match rest.find('\n') {
235 None => {
236 result.push_str(rest);
237 break;
238 }
239 Some(position) => {
240 result.push_str(&rest[..position + 1]);
241 chars.nth(position);
242 }
243 }
244 }
245
246 // Beginning of the line
247 let first_non_ws_ch = 'ws_loop: loop {
248 let maybe_whitespace = chars.next();
249 match maybe_whitespace {
250 None => break 'parse_loop,
251 Some(ch) if ch.is_whitespace() => continue,
252 Some(ch) => break 'ws_loop ch,
253 }
254 };
255
256 match style {
257 CommentStyle::Starred if first_non_ws_ch == '*' => {
258 if &chars.as_str()[..1] == "/" {
259 break;
260 }
261 }
262 CommentStyle::Starred => result.push(first_non_ws_ch),
263 CommentStyle::SingleLine => {
264 assert!(first_non_ws_ch == '/');
265 let rest = chars.as_str();
266 if &rest[..2] == "//" {
267 chars.nth(1);
268 } else if &rest[..1] == "/" {
269 chars.nth(0);
270 } else {
271 panic!("Could not parse comment");
272 }
273 }
274 }
275 }
276
277 Self(result)
278 }
279}
280
281impl From<clang::Usr> for Usr {
282 fn from(usr: clang::Usr) -> Self {
283 Self(usr.0)
284 }
285}
286
287trait FromClangEntity {
288 /// Is responsible for inserting its documentation into the entities manager
289 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self;
290}
291
292trait IntoPoseidocEntity<T> {
293 /// Useful for the `clang_entity.into_poseidoc_entity(&mut manager)` syntax. Implement
294 /// `FromClangEntity` instead.
295 fn into_poseidoc_entity(self, manager: &mut EntitiesManager) -> T;
296}
297
298impl<T> IntoPoseidocEntity<T> for clang::Entity<'_>
299where
300 T: FromClangEntity,
301{
302 fn into_poseidoc_entity(self, manager: &mut EntitiesManager) -> T {
303 T::from_clang_entity(self, manager)
304 }
305}
306
307fn get_description(entity: &clang::Entity) -> Description {
308 let name = entity.get_display_name().unwrap();
309
310 if let (Some(brief), Some(comment)) = (entity.get_comment_brief(), entity.get_comment()) {
311 Description {
312 name,
313 brief,
314 detailed: Comment::from_raw(comment).0,
315 }
316 } else {
317 Description::with_name(name)
318 }
319}
320
321impl FromClangEntity for Entity {
322 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
323 use clang::EntityKind;
324
325 match entity.get_kind() {
326 EntityKind::Namespace => Self::NameSpace(entity.into_poseidoc_entity(manager)),
327 EntityKind::FieldDecl | EntityKind::VarDecl => {
328 Self::Variable(entity.into_poseidoc_entity(manager))
329 }
330 EntityKind::FunctionDecl | EntityKind::Method | EntityKind::FunctionTemplate => {
331 Self::Function(entity.into_poseidoc_entity(manager))
332 }
333 EntityKind::ClassDecl | EntityKind::StructDecl | EntityKind::ClassTemplate => {
334 Self::Class(entity.into_poseidoc_entity(manager))
335 }
336 _ => panic!("Unsupported entity: {:?}", entity),
337 }
338 }
339}
340
341impl FromClangEntity for NameSpace {
342 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
343 match entity.get_kind() {
344 clang::EntityKind::Namespace => {}
345 _ => panic!("Trying to parse a non-variable into a variable"),
346 }
347 debug!("Parsing NameSpace");
348
349 let members = entity
350 .get_children()
351 .into_iter()
352 .map(|child| {
353 let usr = child.get_usr().unwrap().into();
354 trace!("Namespace has member: {:?}", usr);
355 (usr, child.into_poseidoc_entity(manager))
356 })
357 .collect();
358
359 manager.insert(entity.get_usr().unwrap().into(), get_description(&entity));
360
361 NameSpace { members }
362 }
363}
364
365impl FromClangEntity for Variable {
366 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
367 match entity.get_kind() {
368 clang::EntityKind::VarDecl | clang::EntityKind::FieldDecl => {}
369 _ => panic!("Trying to parse a non-variable into a variable"),
370 }
371 debug!("Parsing Variable");
372
373 let r#type = entity.get_type().unwrap().get_display_name();
374 trace!("Variable has type: {:?}", r#type);
375
376 manager.insert(entity.get_usr().unwrap().into(), get_description(&entity));
377
378 Variable { r#type }
379 }
380}
381
382impl FromClangEntity for Function {
383 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
384 match entity.get_kind() {
385 clang::EntityKind::Method
386 | clang::EntityKind::FunctionDecl
387 | clang::EntityKind::FunctionTemplate => {}
388 _ => panic!("Trying to parse a non-function into a function"),
389 }
390 debug!("Parsing Function");
391
392 let return_type = entity.get_result_type().unwrap().get_display_name();
393 trace!("Function has return type: {:?}", return_type);
394 let arguments = entity
395 .get_arguments()
396 .unwrap()
397 .into_iter()
398 .map(|arg| {
399 let name = arg.get_display_name().unwrap();
400 let r#type = arg.get_type().unwrap().get_display_name();
401 trace!("Function has argument {:?} of type {:?}", name, r#type);
402 FunctionArgument { name, r#type }
403 })
404 .collect();
405
406 manager.insert(entity.get_usr().unwrap().into(), get_description(&entity));
407
408 Function {
409 arguments,
410 return_type,
411 }
412 }
413}
414
415impl FromClangEntity for Class {
416 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
417 match entity.get_kind() {
418 clang::EntityKind::ClassDecl
419 | clang::EntityKind::StructDecl
420 | clang::EntityKind::ClassTemplate => {}
421 _ => panic!("Trying to parse a non-class into a class"),
422 }
423 debug!("Parsing Class");
424
425 let mut member_types = Vec::new();
426 let mut member_functions = HashMap::new();
427 let mut member_variables = HashMap::new();
428
429 for child in entity.get_children() {
430 trace!("Class has child: {:?}", child);
431
432 let child_usr = child.get_usr().unwrap().into();
433
434 use clang::EntityKind;
435 match child.get_kind() {
436 EntityKind::ClassDecl | EntityKind::StructDecl | EntityKind::TypeAliasDecl => {
437 member_types.push(child_usr);
438 }
439 EntityKind::Method => {
440 member_functions.insert(child_usr, child.into_poseidoc_entity(manager));
441 }
442 EntityKind::FieldDecl => {
443 member_variables.insert(child_usr, child.into_poseidoc_entity(manager));
444 }
445 _ => trace!("Skipping child"),
446 }
447 }
448
449 manager.insert(entity.get_usr().unwrap().into(), get_description(&entity));
450
451 Class {
452 member_types,
453 member_functions,
454 member_variables,
455 }
456 }
457}