summaryrefslogtreecommitdiffstats
path: root/src/generator.rs
diff options
context:
space:
mode:
authorMinijackson <minijackson@riseup.net>2019-11-23 19:27:21 +0100
committerMinijackson <minijackson@riseup.net>2019-11-23 19:31:58 +0100
commit860b73f1644ecd6548ae403ec483625fb7b625ea (patch)
tree1e0ab5ecf2a77a66e2a176364ecb151a58468426 /src/generator.rs
parent304d9f220cc208dd78fce11b703354ecd8d2168c (diff)
downloadposeidoc-860b73f1644ecd6548ae403ec483625fb7b625ea.tar.gz
poseidoc-860b73f1644ecd6548ae403ec483625fb7b625ea.zip
entities rework, allow "inline" documentation, merge config with cli
Diffstat (limited to 'src/generator.rs')
-rw-r--r--src/generator.rs146
1 files changed, 78 insertions, 68 deletions
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}