summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.rs54
-rw-r--r--src/entities.rs142
-rw-r--r--src/generator.rs146
-rw-r--r--src/main.rs16
-rw-r--r--src/pandoc.rs204
-rw-r--r--src/pandoc/types.rs39
-rw-r--r--src/parsing.rs55
7 files changed, 353 insertions, 303 deletions
diff --git a/src/config.rs b/src/config.rs
index 4463479..b448469 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,3 +1,5 @@
1use crate::cli::Cli;
2
1use anyhow::{anyhow, Context, Result}; 3use anyhow::{anyhow, Context, Result};
2use codemap::CodeMap; 4use codemap::CodeMap;
3use codemap_diagnostic::Diagnostic; 5use codemap_diagnostic::Diagnostic;
@@ -15,19 +17,61 @@ pub(super) const DEFAULT_PROJECT_CONFIGURATION_FILE_NAME: &str = "poseidoc.toml"
15#[serde(rename_all = "kebab-case", default)] 17#[serde(rename_all = "kebab-case", default)]
16pub(crate) struct Config { 18pub(crate) struct Config {
17 #[structopt(skip)] 19 #[structopt(skip)]
18 pub(crate) name: String, 20 pub(crate) name: Option<String>,
19 #[structopt(long, default_value = ".")] 21 #[structopt(long)]
20 pub(crate) compile_commands_location: PathBuf, 22 pub(crate) compile_commands_location: Option<PathBuf>,
21 #[structopt(skip = vec![])] 23 #[structopt(skip = vec![])]
22 pub(crate) extra_clang_args: Vec<String>, 24 pub(crate) extra_clang_args: Vec<String>,
25 #[structopt(flatten)]
26 pub(crate) class: ClassConfig,
27}
28
29impl Config {
30 pub(crate) fn merge_cli(self, cli: Cli) -> Self {
31 Config {
32 name: cli.common_options.name.or(self.name),
33 compile_commands_location: cli
34 .common_options
35 .compile_commands_location
36 .or(self.compile_commands_location),
37 extra_clang_args: cli
38 .extra_arg
39 .into_iter()
40 .flatten()
41 .chain(self.extra_clang_args)
42 .collect(),
43 class: self.class.merge_cli(cli.common_options.class),
44 }
45 }
23} 46}
24 47
25impl Default for Config { 48impl Default for Config {
26 fn default() -> Self { 49 fn default() -> Self {
27 Config { 50 Config {
28 name: "My Project".into(), 51 name: Some(String::from("My Project")),
29 compile_commands_location: PathBuf::from(r"."), 52 compile_commands_location: Some(PathBuf::from(r".")),
30 extra_clang_args: Vec::new(), 53 extra_clang_args: Vec::new(),
54 class: ClassConfig::default(),
55 }
56 }
57}
58
59#[derive(Debug, Clone, StructOpt, Deserialize, Serialize)]
60#[structopt(rename_all = "kebab-case")]
61#[serde(rename_all = "kebab-case", default)]
62pub(crate) struct ClassConfig {
63}
64
65impl ClassConfig {
66 fn merge_cli(self, _cli: ClassConfig) -> Self {
67 ClassConfig {
68 }
69 }
70}
71
72impl Default for ClassConfig {
73 fn default() -> Self {
74 ClassConfig {
31 } 75 }
32 } 76 }
33} 77}
diff --git a/src/entities.rs b/src/entities.rs
index b7368df..89905c6 100644
--- a/src/entities.rs
+++ b/src/entities.rs
@@ -1,16 +1,17 @@
1use std::collections::HashMap; 1use std::collections::HashMap;
2 2
3pub(crate) type DynEntity = dyn Entity + Sync + Send;
4
3#[derive(Debug, Clone, Hash, PartialEq, Eq)] 5#[derive(Debug, Clone, Hash, PartialEq, Eq)]
4pub(crate) struct Usr(pub(crate) String); 6pub(crate) struct Usr(pub(crate) String);
5 7
6#[derive(Debug, Clone)]
7pub(crate) struct EntitiesManager { 8pub(crate) struct EntitiesManager {
8 toplevel_entities: HashMap<Usr, Entity>, 9 toplevel_entities: HashMap<Usr, Box<DynEntity>>,
9 descriptions: HashMap<Usr, Description>, 10 descriptions: HashMap<Usr, Description>,
10} 11}
11 12
12pub(crate) struct EntitiesManagerComponents { 13pub(crate) struct EntitiesManagerComponents {
13 pub(crate) toplevel_entities: HashMap<Usr, Entity>, 14 pub(crate) toplevel_entities: HashMap<Usr, Box<DynEntity>>,
14 pub(crate) descriptions: HashMap<Usr, Description>, 15 pub(crate) descriptions: HashMap<Usr, Description>,
15} 16}
16 17
@@ -27,7 +28,7 @@ impl EntitiesManager {
27 self.descriptions.insert(usr, description); 28 self.descriptions.insert(usr, description);
28 } 29 }
29 30
30 pub fn insert_toplevel(&mut self, usr: Usr, entity: Entity) { 31 pub fn insert_toplevel(&mut self, usr: Usr, entity: Box<DynEntity>) {
31 self.toplevel_entities.insert(usr, entity); 32 self.toplevel_entities.insert(usr, entity);
32 } 33 }
33 34
@@ -61,33 +62,88 @@ pub(crate) struct Described<T> {
61 pub(crate) entity: T, 62 pub(crate) entity: T,
62} 63}
63 64
65pub(crate) struct ChildGroup<'a> {
66 pub(crate) name: &'static str,
67 pub(crate) children: Vec<(&'a Usr, &'a DynEntity)>,
68}
69
70pub(crate) trait Entity {
71 fn kind_name(&self) -> &'static str;
72 fn embeddable_children(&self) -> Vec<ChildGroup> {
73 Vec::new()
74 }
75 fn separate_children(&self) -> Vec<ChildGroup> {
76 Vec::new()
77 }
78}
79
64#[derive(Debug, Clone)] 80#[derive(Debug, Clone)]
65pub(crate) enum Entity { 81pub(crate) struct NameSpace {
82 // TODO: replace with Vec to keep order
83 pub(crate) members: HashMap<Usr, NameSpaceChild>,
84}
85
86#[derive(Debug, Clone)]
87pub(crate) enum NameSpaceChild {
66 NameSpace(NameSpace), 88 NameSpace(NameSpace),
89 Class(Class),
67 Variable(Variable), 90 Variable(Variable),
68 Function(Function), 91 Function(Function),
69 Class(Class),
70} 92}
71 93
72impl Entity { 94impl Entity for NameSpace {
73 fn kind_name(&self) -> &'static str { 95 fn kind_name(&self) -> &'static str {
74 match self { 96 "namespace"
75 Entity::NameSpace(_) => "namespace", 97 }
76 Entity::Variable(_) => "variable", 98
77 Entity::Function(_) => "function", 99 fn embeddable_children(&self) -> Vec<ChildGroup> {
78 Entity::Class(_) => "class", 100 let mut variables = Vec::new();
101 let mut functions = Vec::new();
102 for (usr, member) in &self.members {
103 match member {
104 NameSpaceChild::NameSpace(_) => {}
105 NameSpaceChild::Class(_) => {}
106 NameSpaceChild::Variable(variable) => variables.push((usr, variable as &DynEntity)),
107 NameSpaceChild::Function(function) => functions.push((usr, function as &DynEntity)),
108 }
79 } 109 }
110
111 vec![
112 ChildGroup {
113 name: "Variables",
114 children: variables,
115 },
116 ChildGroup {
117 name: "Functions",
118 children: functions,
119 },
120 ]
80 } 121 }
81}
82 122
83#[derive(Debug, Clone)] 123 fn separate_children(&self) -> Vec<ChildGroup> {
84pub(crate) struct NameSpace { 124 let mut namespaces = Vec::new();
85 pub(crate) members: HashMap<Usr, Entity>, 125 let mut classes = Vec::new();
86} 126 for (usr, member) in &self.members {
127 match member {
128 NameSpaceChild::NameSpace(namespace) => {
129 namespaces.push((usr, namespace as &DynEntity))
130 }
131 NameSpaceChild::Class(class) => classes.push((usr, class as &DynEntity)),
132 NameSpaceChild::Variable(_) => {}
133 NameSpaceChild::Function(_) => {}
134 }
135 }
87 136
88impl From<NameSpace> for Entity { 137 vec![
89 fn from(ns: NameSpace) -> Self { 138 ChildGroup {
90 Entity::NameSpace(ns) 139 name: "Namespaces",
140 children: namespaces,
141 },
142 ChildGroup {
143 name: "Classes",
144 children: classes,
145 },
146 ]
91 } 147 }
92} 148}
93 149
@@ -96,9 +152,9 @@ pub(crate) struct Variable {
96 pub(crate) r#type: String, 152 pub(crate) r#type: String,
97} 153}
98 154
99impl From<Variable> for Entity { 155impl Entity for Variable {
100 fn from(var: Variable) -> Self { 156 fn kind_name(&self) -> &'static str {
101 Entity::Variable(var) 157 "variable"
102 } 158 }
103} 159}
104 160
@@ -108,9 +164,9 @@ pub(crate) struct Function {
108 pub(crate) return_type: String, 164 pub(crate) return_type: String,
109} 165}
110 166
111impl From<Function> for Entity { 167impl Entity for Function {
112 fn from(func: Function) -> Self { 168 fn kind_name(&self) -> &'static str {
113 Entity::Function(func) 169 "function"
114 } 170 }
115} 171}
116 172
@@ -122,13 +178,39 @@ pub(crate) struct FunctionArgument {
122 178
123#[derive(Debug, Clone)] 179#[derive(Debug, Clone)]
124pub(crate) struct Class { 180pub(crate) struct Class {
125 pub(crate) member_types: Vec<Usr>, 181 //pub(crate) member_types: Vec<Usr>,
182 // TODO: replace with Vec to keep order
126 pub(crate) member_functions: HashMap<Usr, Function>, 183 pub(crate) member_functions: HashMap<Usr, Function>,
127 pub(crate) member_variables: HashMap<Usr, Variable>, 184 pub(crate) member_variables: HashMap<Usr, Variable>,
128} 185}
129 186
130impl From<Class> for Entity { 187impl Entity for Class {
131 fn from(class: Class) -> Self { 188 fn kind_name(&self) -> &'static str {
132 Entity::Class(class) 189 "class"
190 }
191
192 fn embeddable_children(&self) -> Vec<ChildGroup> {
193 vec![
194 ChildGroup {
195 name: "Functions",
196 children: self
197 .member_functions
198 .iter()
199 .map(|(usr, func)| (usr, func as &DynEntity))
200 .collect(),
201 },
202 ChildGroup {
203 name: "Variables",
204 children: self
205 .member_variables
206 .iter()
207 .map(|(usr, var)| (usr, var as &DynEntity))
208 .collect(),
209 },
210 ]
211 }
212
213 fn separate_children(&self) -> Vec<ChildGroup> {
214 Vec::new()
133 } 215 }
134} 216}
diff --git a/src/generator.rs b/src/generator.rs
index 0ab4511..0fa5a94 100644
--- a/src/generator.rs
+++ b/src/generator.rs
@@ -1,16 +1,20 @@
1use crate::config::Config;
1use crate::entities::{ 2use crate::entities::{
2 Described, Description, EntitiesManager, EntitiesManagerComponents, Entity, Usr, 3 Description, DynEntity, EntitiesManager, EntitiesManagerComponents, Usr,
3}; 4};
5use crate::pandoc::into_pandoc;
4 6
5use anyhow::{Context, Result}; 7use anyhow::{ensure, Context, Result};
8use rayon::Scope;
6use thiserror::Error; 9use thiserror::Error;
7use threadpool::ThreadPool;
8 10
9use std::collections::HashMap; 11use std::collections::HashMap;
12use std::io::Write;
10use std::path::Path; 13use std::path::Path;
11use std::sync::{mpsc::channel, Arc};
12 14
13pub(crate) fn generate(base_dir: &Path, manager: EntitiesManager) -> Result<()> { 15const DEFAULT_CSS: &[u8] = include_bytes!("../res/style.css");
16
17pub(crate) fn generate(base_dir: &Path, manager: EntitiesManager, config: &Config) -> Result<()> {
14 let EntitiesManagerComponents { 18 let EntitiesManagerComponents {
15 toplevel_entities, 19 toplevel_entities,
16 descriptions, 20 descriptions,
@@ -24,87 +28,78 @@ pub(crate) fn generate(base_dir: &Path, manager: EntitiesManager) -> Result<()>
24 std::fs::create_dir_all(&html_output_dir) 28 std::fs::create_dir_all(&html_output_dir)
25 .context("Failed to create the HTML output directory")?; 29 .context("Failed to create the HTML output directory")?;
26 30
27 let pool = ThreadPool::new(num_cpus::get()); 31 let mut css_tempfile = tempfile::Builder::new()
28 32 .prefix("style")
29 let descriptions = Arc::new(descriptions); 33 .suffix(".css")
30 34 .tempfile()?;
31 let (tx, rx) = channel::<()>(); 35 css_tempfile.write_all(DEFAULT_CSS)?;
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 36
45 drop(tx); 37 let css_path = css_tempfile.path();
38 debug!("Generated temporary file with CSS at: {:?}", css_path);
46 39
47 // This is not really idiomatic, but iter returns None when every Sender is destroyed, so just 40 rayon::scope(|scope| {
48 // by passing around Senders in generate_recursively, we wait for every job. 41 for (usr, entity) in &toplevel_entities {
49 rx.iter().for_each(drop); 42 generate_recursively(
43 &usr,
44 entity.as_ref(),
45 scope,
46 &descriptions,
47 &md_output_dir,
48 &html_output_dir,
49 css_path,
50 );
51 }
52 });
50 53
51 Ok(()) 54 Ok(())
52} 55}
53 56
54fn generate_recursively( 57fn generate_recursively<'a>(
55 usr: Usr, 58 usr: &'a Usr,
56 entity: Entity, 59 entity: &'a DynEntity,
57 pool: ThreadPool, 60 pool: &Scope<'a>,
58 tx: std::sync::mpsc::Sender<()>, 61 descriptions: &'a HashMap<Usr, Description>,
59 descriptions: Arc<HashMap<Usr, Description>>, 62 md_output_dir: &'a Path,
60 md_output_dir: impl AsRef<Path>, 63 html_output_dir: &'a Path,
61 html_output_dir: impl AsRef<Path>, 64 css_path: &'a Path,
62) { 65) {
63 let descriptions = descriptions.clone(); 66 pool.spawn(move |pool| {
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); 67 trace!("Trying to generate {}", usr.0);
70 68
71 let entity = Described::<Entity> {
72 entity,
73 description: descriptions.get(&usr).unwrap().clone(),
74 };
75
76 let leftovers = generate_single( 69 let leftovers = generate_single(
77 &usr, 70 &usr,
78 entity, 71 entity,
72 descriptions.get(&usr).unwrap(),
79 &descriptions, 73 &descriptions,
80 &md_output_dir, 74 &md_output_dir,
81 &html_output_dir, 75 &html_output_dir,
76 &css_path,
82 ) 77 )
83 .unwrap(); 78 .unwrap();
84 79
85 for (usr, entity) in leftovers { 80 for (usr, entity) in leftovers {
86 let pool = pool2.clone();
87 let tx = tx.clone();
88 generate_recursively( 81 generate_recursively(
89 usr, 82 usr,
90 entity, 83 entity,
91 pool, 84 pool,
92 tx, 85 descriptions,
93 descriptions.clone(), 86 md_output_dir,
94 md_output_dir.clone(), 87 html_output_dir,
95 html_output_dir.clone(), 88 css_path,
96 ); 89 );
97 } 90 }
98 }); 91 });
99} 92}
100 93
101fn generate_single( 94fn generate_single<'e>(
102 usr: &Usr, 95 usr: &Usr,
103 entity: Described<Entity>, 96 entity: &'e DynEntity,
97 description: &Description,
104 descriptions: &HashMap<Usr, Description>, 98 descriptions: &HashMap<Usr, Description>,
105 md_output_dir: impl AsRef<Path>, 99 md_output_dir: impl AsRef<Path>,
106 html_output_dir: impl AsRef<Path>, 100 html_output_dir: impl AsRef<Path>,
107) -> Result<HashMap<Usr, Entity>> { 101 css_path: impl AsRef<Path>,
102) -> Result<HashMap<&'e Usr, &'e DynEntity>> {
108 use std::io::prelude::*; 103 use std::io::prelude::*;
109 use std::process::{Command, Stdio}; 104 use std::process::{Command, Stdio};
110 105
@@ -132,7 +127,7 @@ fn generate_single(
132 .spawn() 127 .spawn()
133 .context("Failed to execute Pandoc command")?; 128 .context("Failed to execute Pandoc command")?;
134 129
135 let (pandoc_ast, leftovers) = entity.into_pandoc(&descriptions); 130 let (pandoc_ast, leftovers) = into_pandoc(entity, description, descriptions);
136 131
137 if log_enabled!(log::Level::Trace) { 132 if log_enabled!(log::Level::Trace) {
138 let json = 133 let json =
@@ -152,17 +147,20 @@ fn generate_single(
152 .context("Failed to convert Pandoc AST to JSON")?; 147 .context("Failed to convert Pandoc AST to JSON")?;
153 } 148 }
154 149
150 let command_str = format!("{:?}", pandoc);
151
155 let output = pandoc 152 let output = pandoc
156 .wait_with_output() 153 .wait_with_output()
157 .expect("Pandoc command wasn't running"); 154 .expect("Pandoc command wasn't running");
158 155
159 if !output.status.success() { 156 ensure!(
160 Err(CommandError { 157 output.status.success(),
158 CommandError {
159 command: format!("{:?}", command_str),
161 status: output.status, 160 status: output.status,
162 stderr: String::from_utf8(output.stderr).expect("Pandoc outputted invalid UTF-8"), 161 stderr: String::from_utf8(output.stderr).expect("Pandoc outputted invalid UTF-8"),
163 }) 162 }
164 .context("Pandoc command failed")?; 163 );
165 }
166 164
167 let mut command = Command::new("pandoc"); 165 let mut command = Command::new("pandoc");
168 command 166 command
@@ -172,7 +170,11 @@ fn generate_single(
172 .args(&[ 170 .args(&[
173 "--from=markdown", 171 "--from=markdown",
174 "--to=html", 172 "--to=html",
175 "--css=res/style.css", 173 "--css",
174 css_path
175 .as_ref()
176 .to_str()
177 .context("CSS path is not valid UTF-8")?,
176 "--standalone", 178 "--standalone",
177 "--self-contained", 179 "--self-contained",
178 md_output_file 180 md_output_file
@@ -184,26 +186,34 @@ fn generate_single(
184 .context("Entity name is not valid UTF-8")?, 186 .context("Entity name is not valid UTF-8")?,
185 ]); 187 ]);
186 188
187 debug!("Launching command: {:?}", command); 189 let command_str = format!("{:?}", command);
190 debug!("Launching command: {}", command_str);
188 191
189 let output = command 192 let output = command
190 .output() 193 .output()
191 .context("Failed to execute Pandoc command")?; 194 .context("Failed to execute Pandoc command")?;
192 195
193 if !output.status.success() { 196 ensure!(
194 Err(CommandError { 197 output.status.success(),
198 CommandError {
199 command: command_str,
195 status: output.status, 200 status: output.status,
196 stderr: String::from_utf8(output.stderr).expect("Pandoc outputted invalid UTF-8"), 201 stderr: String::from_utf8(output.stderr).expect("Pandoc outputted invalid UTF-8"),
197 }) 202 }
198 .context("Pandoc command failed")?; 203 );
199 }
200 204
201 Ok(leftovers) 205 Ok(leftovers)
202} 206}
203 207
204#[derive(Debug, Clone, Error)] 208#[derive(Debug, Clone, Error)]
205#[error("Command returned status {:?} and stderr {:?}", status, stderr)] 209#[error(
210 "Command {} returned status {:?} and stderr {:?}",
211 command,
212 status,
213 stderr
214)]
206struct CommandError { 215struct CommandError {
216 command: String,
207 status: std::process::ExitStatus, 217 status: std::process::ExitStatus,
208 stderr: String, 218 stderr: String,
209} 219}
diff --git a/src/main.rs b/src/main.rs
index 503688e..e6e36d9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,5 @@
1#![warn(clippy::all)]
2
1//mod doxygen; 3//mod doxygen;
2mod cli; 4mod cli;
3mod config; 5mod config;
@@ -83,13 +85,15 @@ fn start(codemap: &mut CodeMap) -> Result<()> {
83 std::env::set_current_dir(&cli.directory) 85 std::env::set_current_dir(&cli.directory)
84 .with_context(|| format!("Cannot change current directory to: {:?}", cli.directory))?; 86 .with_context(|| format!("Cannot change current directory to: {:?}", cli.directory))?;
85 87
86 match cli.command { 88 match &cli.command {
87 Command::Generate { file } => { 89 Command::Generate { file } => {
88 let extra_args = cli.extra_arg.iter().flatten().map(AsRef::as_ref).collect(); 90 let file = file.clone();
89 let manager = parse_file(file, extra_args); 91 let config = load_effective_config(cli, codemap)?;
92
93 let manager = parse_file(file, &config.extra_clang_args);
90 94
91 let base_output_dir = std::path::Path::new("doc"); 95 let base_output_dir = std::path::Path::new("doc");
92 generate(&base_output_dir, manager)?; 96 generate(&base_output_dir, manager, &config)?;
93 } 97 }
94 Command::Config { 98 Command::Config {
95 command: ConfigCommand::Default, 99 command: ConfigCommand::Default,
@@ -108,3 +112,7 @@ fn start(codemap: &mut CodeMap) -> Result<()> {
108 112
109 Ok(()) 113 Ok(())
110} 114}
115
116fn load_effective_config(cli: cli::Cli, codemap: &mut CodeMap) -> Result<config::Config> {
117 Ok(config::load_config(".", codemap)?.merge_cli(cli))
118}
diff --git a/src/pandoc.rs b/src/pandoc.rs
index 5ca84e7..8477f28 100644
--- a/src/pandoc.rs
+++ b/src/pandoc.rs
@@ -1,186 +1,106 @@
1//mod types;
2
3use crate::entities::*; 1use crate::entities::*;
4 2
5use pandoc_types::definition::{Attr, Block, Inline, Meta, MetaValue, Pandoc}; 3use pandoc_types::definition::{Attr, Block, Inline, Meta, MetaValue, Pandoc};
6 4
7use std::collections::HashMap; 5use std::collections::HashMap;
8 6
9impl Described<Entity> { 7pub(crate) fn into_pandoc<'e>(
10 pub fn into_pandoc( 8 entity: &'e dyn Entity,
11 self, 9 description: &Description,
12 descriptions: &HashMap<Usr, Description>, 10 descriptions: &HashMap<Usr, Description>,
13 ) -> (Pandoc, HashMap<Usr, Entity>) { 11) -> (Pandoc, HashMap<&'e Usr, &'e DynEntity>) {
14 let mut meta = Meta::null(); 12 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 13
78impl PandocDisplay for Described<Entity> { 14 let title = vec![Inline::Code(Attr::null(), description.name.clone())];
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 15
88 fn content_after(&self, descriptions: &HashMap<Usr, Description>) -> Vec<Block> { 16 meta.0.insert(
89 match &self.entity { 17 "title".to_string(),
90 Entity::NameSpace(ns) => ns.content_after(descriptions), 18 MetaValue::MetaString(description.name.clone()),
91 Entity::Variable(var) => var.content_after(descriptions), 19 );
92 Entity::Function(func) => func.content_after(descriptions),
93 Entity::Class(class) => class.content_after(descriptions),
94 }
95 }
96 20
97 fn leftovers(&self) -> HashMap<Usr, Entity> { 21 let mut content = Vec::new();
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 22
107impl PandocDisplay for NameSpace { 23 content.push(Block::Header(1, Attr::null(), title));
108 fn content_after(&self, descriptions: &HashMap<Usr, Description>) -> Vec<Block> {
109 let mut content = Vec::new();
110 24
25 if !description.detailed.is_empty() {
111 content.push(Block::Header( 26 content.push(Block::Header(
112 2, 27 2,
113 Attr::null(), 28 Attr::null(),
114 vec![Inline::Str("Members".to_string())], 29 vec![Inline::Str(String::from("Description"))],
115 )); 30 ));
116 31
117 if let Some(member_list) = member_list(self.members.keys(), descriptions) { 32 content.push(Block::Div(
118 content.push(member_list); 33 Attr(String::new(), vec![String::from("doc")], vec![]),
119 } else { 34 vec![raw_markdown(description.detailed.clone())],
120 content.push(str_block(String::from("None"))); 35 ));
121 }
122
123 content
124 }
125
126 fn leftovers(&self) -> HashMap<Usr, Entity> {
127 self.members.clone()
128 } 36 }
129}
130 37
131impl PandocDisplay for Class { 38 let separate_children = entity.separate_children();
132 fn content_after(&self, descriptions: &HashMap<Usr, Description>) -> Vec<Block> { 39 let embeddable_children = entity.embeddable_children();
133 let mut content = Vec::new();
134 40
135 if let Some(member_types) = member_list(&self.member_types, descriptions) { 41 for section in &separate_children {
42 if let Some(members_list) = member_list(
43 section.children.iter().map(|&(usr, _child)| usr),
44 descriptions,
45 ) {
136 content.push(Block::Header( 46 content.push(Block::Header(
137 2, 47 2,
138 Attr::null(), 48 Attr::null(),
139 vec![Inline::Str("Member Types".to_string())], 49 vec![Inline::Str(String::from(section.name))],
140 )); 50 ));
141 51
142 content.push(member_types); 52 content.push(members_list);
143 } 53 }
54 }
55
56 let mut embedded_documentation = Vec::new();
144 57
145 if let Some(member_functions) = member_list(self.member_functions.keys(), descriptions) { 58 for section in &embeddable_children {
59 if let Some(members_list) = member_list(
60 section.children.iter().map(|&(usr, _child)| usr),
61 descriptions,
62 ) {
146 content.push(Block::Header( 63 content.push(Block::Header(
147 2, 64 2,
148 Attr::null(), 65 Attr::null(),
149 vec![Inline::Str("Member Functions".to_string())], 66 vec![Inline::Str(String::from(section.name))],
150 )); 67 ));
151 68
152 content.push(member_functions); 69 content.push(members_list);
153 }
154 70
155 if let Some(member_variables) = member_list(self.member_variables.keys(), descriptions) { 71 embedded_documentation.push(Block::Header(
156 content.push(Block::Header(
157 2, 72 2,
158 Attr::null(), 73 Attr::null(),
159 vec![Inline::Str("Member Variables".to_string())], 74 vec![Inline::Str(String::from(section.name) + " Documentation")],
160 )); 75 ));
161 76
162 content.push(member_variables); 77 for (usr, _child) in &section.children {
163 } 78 let child_doc = descriptions.get(usr).unwrap();
164 79
165 content 80 embedded_documentation.push(Block::Header(
166 } 81 3,
82 Attr::null(),
83 vec![Inline::Code(Attr::null(), String::from(&child_doc.name))],
84 ));
167 85
168 fn leftovers(&self) -> HashMap<Usr, Entity> { 86 embedded_documentation.push(Block::Div(
169 self.member_functions 87 Attr(String::new(), vec![String::from("doc")], vec![]),
170 .iter() 88 vec![raw_markdown(child_doc.detailed.clone())],
171 .map(|(usr, func)| (usr.clone(), Entity::from(func.clone()))) 89 ));
172 .chain( 90 }
173 self.member_variables 91 }
174 .iter()
175 .map(|(usr, var)| (usr.clone(), Entity::from(var.clone()))),
176 )
177 .collect()
178 } 92 }
179}
180 93
181impl PandocDisplay for Variable {} 94 content.append(&mut embedded_documentation);
182 95
183impl PandocDisplay for Function {} 96 let leftovers = separate_children
97 .iter()
98 .map(|section| section.children.clone())
99 .flatten()
100 .collect();
101
102 (Pandoc(meta, content), leftovers)
103}
184 104
185fn str_block(content: String) -> Block { 105fn str_block(content: String) -> Block {
186 Block::Plain(vec![Inline::Str(content)]) 106 Block::Plain(vec![Inline::Str(content)])
diff --git a/src/pandoc/types.rs b/src/pandoc/types.rs
deleted file mode 100644
index dc5be64..0000000
--- a/src/pandoc/types.rs
+++ /dev/null
@@ -1,39 +0,0 @@
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
index 137b89d..d7aaa49 100644
--- a/src/parsing.rs
+++ b/src/parsing.rs
@@ -6,11 +6,13 @@ use codemap::CodeMap;
6use std::collections::HashMap; 6use std::collections::HashMap;
7use std::path::{Path, PathBuf}; 7use std::path::{Path, PathBuf};
8 8
9pub(crate) fn parse_file<T>(path: T, mut extra_args: Vec<&str>) -> EntitiesManager 9pub(crate) fn parse_file<T, S>(path: T, extra_args: &[S]) -> EntitiesManager
10where 10where
11 T: Into<PathBuf>, 11 T: Into<PathBuf>,
12 T: AsRef<Path>, 12 T: AsRef<Path>,
13 T: ToString, 13 T: ToString,
14 S: AsRef<str>,
15 S: std::fmt::Debug,
14{ 16{
15 let mut codemap = CodeMap::new(); 17 let mut codemap = CodeMap::new();
16 let file_map = codemap.add_file(path.to_string(), std::fs::read_to_string(&path).unwrap()); 18 let file_map = codemap.add_file(path.to_string(), std::fs::read_to_string(&path).unwrap());
@@ -21,7 +23,6 @@ where
21 let mut parser = index.parser(path); 23 let mut parser = index.parser(path);
22 parser.skip_function_bodies(true); 24 parser.skip_function_bodies(true);
23 25
24 extra_args.push("-Werror=documentation");
25 parser.arguments(&extra_args); 26 parser.arguments(&extra_args);
26 27
27 if log_enabled!(log::Level::Debug) { 28 if log_enabled!(log::Level::Debug) {
@@ -70,7 +71,7 @@ where
70 let mut emitter = Emitter::stderr(ColorConfig::Auto, Some(&codemap)); 71 let mut emitter = Emitter::stderr(ColorConfig::Auto, Some(&codemap));
71 72
72 for diagnostic in trans_unit.get_diagnostics().iter() { 73 for diagnostic in trans_unit.get_diagnostics().iter() {
73 let main_diag = match clang_diag_to_codemap_diag(&diagnostic, &file_span) { 74 let main_diag = match clang_diag_to_codemap_diag(&diagnostic, file_span) {
74 Some(diag) => diag, 75 Some(diag) => diag,
75 None => continue, 76 None => continue,
76 }; 77 };
@@ -78,12 +79,12 @@ where
78 let sub_diags = diagnostic 79 let sub_diags = diagnostic
79 .get_children() 80 .get_children()
80 .into_iter() 81 .into_iter()
81 .filter_map(|diagnostic| clang_diag_to_codemap_diag(&diagnostic, &file_span)); 82 .filter_map(|diagnostic| clang_diag_to_codemap_diag(&diagnostic, file_span));
82 83
83 let fix_it_diags = diagnostic 84 let fix_it_diags = diagnostic
84 .get_fix_its() 85 .get_fix_its()
85 .into_iter() 86 .into_iter()
86 .map(|fix_it| clang_fix_it_to_codemap_diag(&fix_it, &file_span)); 87 .map(|fix_it| clang_fix_it_to_codemap_diag(&fix_it, file_span));
87 88
88 emitter.emit( 89 emitter.emit(
89 &std::iter::once(main_diag) 90 &std::iter::once(main_diag)
@@ -98,7 +99,7 @@ where
98 99
99fn clang_diag_to_codemap_diag( 100fn clang_diag_to_codemap_diag(
100 diagnostic: &clang::diagnostic::Diagnostic, 101 diagnostic: &clang::diagnostic::Diagnostic,
101 file_span: &codemap::Span, 102 file_span: codemap::Span,
102) -> Option<codemap_diagnostic::Diagnostic> { 103) -> Option<codemap_diagnostic::Diagnostic> {
103 use codemap_diagnostic::{Diagnostic, Level, SpanLabel, SpanStyle}; 104 use codemap_diagnostic::{Diagnostic, Level, SpanLabel, SpanStyle};
104 105
@@ -146,7 +147,7 @@ fn clang_diag_to_codemap_diag(
146 147
147fn clang_fix_it_to_codemap_diag( 148fn clang_fix_it_to_codemap_diag(
148 fix_it: &clang::diagnostic::FixIt, 149 fix_it: &clang::diagnostic::FixIt,
149 file_span: &codemap::Span, 150 file_span: codemap::Span,
150) -> codemap_diagnostic::Diagnostic { 151) -> codemap_diagnostic::Diagnostic {
151 use clang::diagnostic::FixIt; 152 use clang::diagnostic::FixIt;
152 use codemap_diagnostic::{Diagnostic, Level, SpanLabel, SpanStyle}; 153 use codemap_diagnostic::{Diagnostic, Level, SpanLabel, SpanStyle};
@@ -195,6 +196,7 @@ fn clang_fix_it_to_codemap_diag(
195pub(crate) struct Comment(String); 196pub(crate) struct Comment(String);
196 197
197impl Comment { 198impl Comment {
199 // TODO: weirdness with emojis and line returns?
198 pub fn from_raw(raw: String) -> Self { 200 pub fn from_raw(raw: String) -> Self {
199 #[derive(Debug)] 201 #[derive(Debug)]
200 enum CommentStyle { 202 enum CommentStyle {
@@ -237,7 +239,7 @@ impl Comment {
237 break; 239 break;
238 } 240 }
239 Some(position) => { 241 Some(position) => {
240 result.push_str(&rest[..position + 1]); 242 result.push_str(&rest[..=position]);
241 chars.nth(position); 243 chars.nth(position);
242 } 244 }
243 } 245 }
@@ -318,20 +320,20 @@ fn get_description(entity: &clang::Entity) -> Description {
318 } 320 }
319} 321}
320 322
321impl FromClangEntity for Entity { 323impl FromClangEntity for Box<DynEntity> {
322 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self { 324 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
323 use clang::EntityKind; 325 use clang::EntityKind;
324 326
325 match entity.get_kind() { 327 match entity.get_kind() {
326 EntityKind::Namespace => Self::NameSpace(entity.into_poseidoc_entity(manager)), 328 EntityKind::Namespace => Box::new(NameSpace::from_clang_entity(entity, manager)),
327 EntityKind::FieldDecl | EntityKind::VarDecl => { 329 EntityKind::FieldDecl | EntityKind::VarDecl => {
328 Self::Variable(entity.into_poseidoc_entity(manager)) 330 Box::new(Variable::from_clang_entity(entity, manager))
329 } 331 }
330 EntityKind::FunctionDecl | EntityKind::Method | EntityKind::FunctionTemplate => { 332 EntityKind::FunctionDecl | EntityKind::Method | EntityKind::FunctionTemplate => {
331 Self::Function(entity.into_poseidoc_entity(manager)) 333 Box::new(Function::from_clang_entity(entity, manager))
332 } 334 }
333 EntityKind::ClassDecl | EntityKind::StructDecl | EntityKind::ClassTemplate => { 335 EntityKind::ClassDecl | EntityKind::StructDecl | EntityKind::ClassTemplate => {
334 Self::Class(entity.into_poseidoc_entity(manager)) 336 Box::new(Class::from_clang_entity(entity, manager))
335 } 337 }
336 _ => panic!("Unsupported entity: {:?}", entity), 338 _ => panic!("Unsupported entity: {:?}", entity),
337 } 339 }
@@ -362,6 +364,28 @@ impl FromClangEntity for NameSpace {
362 } 364 }
363} 365}
364 366
367impl FromClangEntity for NameSpaceChild {
368 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
369 use clang::EntityKind;
370
371 match entity.get_kind() {
372 EntityKind::Namespace => {
373 NameSpaceChild::NameSpace(NameSpace::from_clang_entity(entity, manager))
374 }
375 EntityKind::FieldDecl | EntityKind::VarDecl => {
376 NameSpaceChild::Variable(Variable::from_clang_entity(entity, manager))
377 }
378 EntityKind::FunctionDecl | EntityKind::Method | EntityKind::FunctionTemplate => {
379 NameSpaceChild::Function(Function::from_clang_entity(entity, manager))
380 }
381 EntityKind::ClassDecl | EntityKind::StructDecl | EntityKind::ClassTemplate => {
382 NameSpaceChild::Class(Class::from_clang_entity(entity, manager))
383 }
384 _ => panic!("Unsupported entity: {:?}", entity),
385 }
386 }
387}
388
365impl FromClangEntity for Variable { 389impl FromClangEntity for Variable {
366 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self { 390 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
367 match entity.get_kind() { 391 match entity.get_kind() {
@@ -383,6 +407,7 @@ impl FromClangEntity for Function {
383 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self { 407 fn from_clang_entity(entity: clang::Entity, manager: &mut EntitiesManager) -> Self {
384 match entity.get_kind() { 408 match entity.get_kind() {
385 clang::EntityKind::Method 409 clang::EntityKind::Method
410 | clang::EntityKind::Constructor
386 | clang::EntityKind::FunctionDecl 411 | clang::EntityKind::FunctionDecl
387 | clang::EntityKind::FunctionTemplate => {} 412 | clang::EntityKind::FunctionTemplate => {}
388 _ => panic!("Trying to parse a non-function into a function"), 413 _ => panic!("Trying to parse a non-function into a function"),
@@ -436,7 +461,7 @@ impl FromClangEntity for Class {
436 EntityKind::ClassDecl | EntityKind::StructDecl | EntityKind::TypeAliasDecl => { 461 EntityKind::ClassDecl | EntityKind::StructDecl | EntityKind::TypeAliasDecl => {
437 member_types.push(child_usr); 462 member_types.push(child_usr);
438 } 463 }
439 EntityKind::Method => { 464 EntityKind::Method | EntityKind::Constructor | EntityKind::FunctionDecl => {
440 member_functions.insert(child_usr, child.into_poseidoc_entity(manager)); 465 member_functions.insert(child_usr, child.into_poseidoc_entity(manager));
441 } 466 }
442 EntityKind::FieldDecl => { 467 EntityKind::FieldDecl => {
@@ -449,7 +474,7 @@ impl FromClangEntity for Class {
449 manager.insert(entity.get_usr().unwrap().into(), get_description(&entity)); 474 manager.insert(entity.get_usr().unwrap().into(), get_description(&entity));
450 475
451 Class { 476 Class {
452 member_types, 477 //member_types,
453 member_functions, 478 member_functions,
454 member_variables, 479 member_variables,
455 } 480 }