diff options
author | Minijackson <minijackson@riseup.net> | 2019-11-23 19:27:21 +0100 |
---|---|---|
committer | Minijackson <minijackson@riseup.net> | 2019-11-23 19:31:58 +0100 |
commit | 860b73f1644ecd6548ae403ec483625fb7b625ea (patch) | |
tree | 1e0ab5ecf2a77a66e2a176364ecb151a58468426 /src/generator.rs | |
parent | 304d9f220cc208dd78fce11b703354ecd8d2168c (diff) | |
download | poseidoc-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.rs | 146 |
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 @@ | |||
1 | use crate::config::Config; | ||
1 | use crate::entities::{ | 2 | use crate::entities::{ |
2 | Described, Description, EntitiesManager, EntitiesManagerComponents, Entity, Usr, | 3 | Description, DynEntity, EntitiesManager, EntitiesManagerComponents, Usr, |
3 | }; | 4 | }; |
5 | use crate::pandoc::into_pandoc; | ||
4 | 6 | ||
5 | use anyhow::{Context, Result}; | 7 | use anyhow::{ensure, Context, Result}; |
8 | use rayon::Scope; | ||
6 | use thiserror::Error; | 9 | use thiserror::Error; |
7 | use threadpool::ThreadPool; | ||
8 | 10 | ||
9 | use std::collections::HashMap; | 11 | use std::collections::HashMap; |
12 | use std::io::Write; | ||
10 | use std::path::Path; | 13 | use std::path::Path; |
11 | use std::sync::{mpsc::channel, Arc}; | ||
12 | 14 | ||
13 | pub(crate) fn generate(base_dir: &Path, manager: EntitiesManager) -> Result<()> { | 15 | const DEFAULT_CSS: &[u8] = include_bytes!("../res/style.css"); |
16 | |||
17 | pub(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 | ||
54 | fn generate_recursively( | 57 | fn 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 | ||
101 | fn generate_single( | 94 | fn 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 | )] | ||
206 | struct CommandError { | 215 | struct CommandError { |
216 | command: String, | ||
207 | status: std::process::ExitStatus, | 217 | status: std::process::ExitStatus, |
208 | stderr: String, | 218 | stderr: String, |
209 | } | 219 | } |