diff options
author | Minijackson <minijackson@riseup.net> | 2019-12-18 20:56:53 +0100 |
---|---|---|
committer | Minijackson <minijackson@riseup.net> | 2019-12-18 20:56:53 +0100 |
commit | de896baff7e97fac4dde79078c9a2fa1c652576b (patch) | |
tree | 512b67b91d64e51d63f7ac5ff925a5c96d9aaf3c /src/generator.rs | |
parent | 860b73f1644ecd6548ae403ec483625fb7b625ea (diff) | |
download | poseidoc-de896baff7e97fac4dde79078c9a2fa1c652576b.tar.gz poseidoc-de896baff7e97fac4dde79078c9a2fa1c652576b.zip |
Big refactoring
- entities should be more coherent when parsing multiple files
- well defined, language agnostic entity tree
- each module has its own configuration
- less dead code
Diffstat (limited to 'src/generator.rs')
-rw-r--r-- | src/generator.rs | 219 |
1 files changed, 0 insertions, 219 deletions
diff --git a/src/generator.rs b/src/generator.rs deleted file mode 100644 index 0fa5a94..0000000 --- a/src/generator.rs +++ /dev/null | |||
@@ -1,219 +0,0 @@ | |||
1 | use crate::config::Config; | ||
2 | use crate::entities::{ | ||
3 | Description, DynEntity, EntitiesManager, EntitiesManagerComponents, Usr, | ||
4 | }; | ||
5 | use crate::pandoc::into_pandoc; | ||
6 | |||
7 | use anyhow::{ensure, Context, Result}; | ||
8 | use rayon::Scope; | ||
9 | use thiserror::Error; | ||
10 | |||
11 | use std::collections::HashMap; | ||
12 | use std::io::Write; | ||
13 | use std::path::Path; | ||
14 | |||
15 | const DEFAULT_CSS: &[u8] = include_bytes!("../res/style.css"); | ||
16 | |||
17 | pub(crate) fn generate(base_dir: &Path, manager: EntitiesManager, config: &Config) -> Result<()> { | ||
18 | let EntitiesManagerComponents { | ||
19 | toplevel_entities, | ||
20 | descriptions, | ||
21 | } = manager.decompose(); | ||
22 | |||
23 | let md_output_dir = base_dir.join("markdown"); | ||
24 | let html_output_dir = base_dir.join("html"); | ||
25 | |||
26 | std::fs::create_dir_all(&md_output_dir) | ||
27 | .context("Failed to create the markdown output directory")?; | ||
28 | std::fs::create_dir_all(&html_output_dir) | ||
29 | .context("Failed to create the HTML output directory")?; | ||
30 | |||
31 | let mut css_tempfile = tempfile::Builder::new() | ||
32 | .prefix("style") | ||
33 | .suffix(".css") | ||
34 | .tempfile()?; | ||
35 | css_tempfile.write_all(DEFAULT_CSS)?; | ||
36 | |||
37 | let css_path = css_tempfile.path(); | ||
38 | debug!("Generated temporary file with CSS at: {:?}", css_path); | ||
39 | |||
40 | rayon::scope(|scope| { | ||
41 | for (usr, entity) in &toplevel_entities { | ||
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 | }); | ||
53 | |||
54 | Ok(()) | ||
55 | } | ||
56 | |||
57 | fn generate_recursively<'a>( | ||
58 | usr: &'a Usr, | ||
59 | entity: &'a DynEntity, | ||
60 | pool: &Scope<'a>, | ||
61 | descriptions: &'a HashMap<Usr, Description>, | ||
62 | md_output_dir: &'a Path, | ||
63 | html_output_dir: &'a Path, | ||
64 | css_path: &'a Path, | ||
65 | ) { | ||
66 | pool.spawn(move |pool| { | ||
67 | trace!("Trying to generate {}", usr.0); | ||
68 | |||
69 | let leftovers = generate_single( | ||
70 | &usr, | ||
71 | entity, | ||
72 | descriptions.get(&usr).unwrap(), | ||
73 | &descriptions, | ||
74 | &md_output_dir, | ||
75 | &html_output_dir, | ||
76 | &css_path, | ||
77 | ) | ||
78 | .unwrap(); | ||
79 | |||
80 | for (usr, entity) in leftovers { | ||
81 | generate_recursively( | ||
82 | usr, | ||
83 | entity, | ||
84 | pool, | ||
85 | descriptions, | ||
86 | md_output_dir, | ||
87 | html_output_dir, | ||
88 | css_path, | ||
89 | ); | ||
90 | } | ||
91 | }); | ||
92 | } | ||
93 | |||
94 | fn generate_single<'e>( | ||
95 | usr: &Usr, | ||
96 | entity: &'e DynEntity, | ||
97 | description: &Description, | ||
98 | descriptions: &HashMap<Usr, Description>, | ||
99 | md_output_dir: impl AsRef<Path>, | ||
100 | html_output_dir: impl AsRef<Path>, | ||
101 | css_path: impl AsRef<Path>, | ||
102 | ) -> Result<HashMap<&'e Usr, &'e DynEntity>> { | ||
103 | use std::io::prelude::*; | ||
104 | use std::process::{Command, Stdio}; | ||
105 | |||
106 | let md_output_file = md_output_dir.as_ref().join(&usr.0); | ||
107 | let html_output_file = html_output_dir.as_ref().join(&usr.0); | ||
108 | |||
109 | let mut command = Command::new("pandoc"); | ||
110 | |||
111 | command | ||
112 | .stdin(Stdio::piped()) | ||
113 | .stdout(Stdio::piped()) | ||
114 | .stderr(Stdio::piped()) | ||
115 | .args(&[ | ||
116 | "--from=json", | ||
117 | "--to=markdown", | ||
118 | "--output", | ||
119 | md_output_file | ||
120 | .to_str() | ||
121 | .context("Entity name is not valid UTF-8")?, | ||
122 | ]); | ||
123 | |||
124 | debug!("Launching command: {:?}", command); | ||
125 | |||
126 | let mut pandoc = command | ||
127 | .spawn() | ||
128 | .context("Failed to execute Pandoc command")?; | ||
129 | |||
130 | let (pandoc_ast, leftovers) = into_pandoc(entity, description, descriptions); | ||
131 | |||
132 | if log_enabled!(log::Level::Trace) { | ||
133 | let json = | ||
134 | serde_json::to_string(&pandoc_ast).context("Failed to convert Pandoc AST to JSON")?; | ||
135 | trace!("Sending json: {}", json); | ||
136 | write!( | ||
137 | pandoc.stdin.as_mut().expect("Pandoc stdin not captured"), | ||
138 | "{}", | ||
139 | &json, | ||
140 | ) | ||
141 | .context("Failed to convert Pandoc AST to JSON")?; | ||
142 | } else { | ||
143 | serde_json::to_writer( | ||
144 | pandoc.stdin.as_mut().expect("Pandoc stdin not captured"), | ||
145 | &pandoc_ast, | ||
146 | ) | ||
147 | .context("Failed to convert Pandoc AST to JSON")?; | ||
148 | } | ||
149 | |||
150 | let command_str = format!("{:?}", pandoc); | ||
151 | |||
152 | let output = pandoc | ||
153 | .wait_with_output() | ||
154 | .expect("Pandoc command wasn't running"); | ||
155 | |||
156 | ensure!( | ||
157 | output.status.success(), | ||
158 | CommandError { | ||
159 | command: format!("{:?}", command_str), | ||
160 | status: output.status, | ||
161 | stderr: String::from_utf8(output.stderr).expect("Pandoc outputted invalid UTF-8"), | ||
162 | } | ||
163 | ); | ||
164 | |||
165 | let mut command = Command::new("pandoc"); | ||
166 | command | ||
167 | .stdin(Stdio::piped()) | ||
168 | .stdout(Stdio::piped()) | ||
169 | .stderr(Stdio::piped()) | ||
170 | .args(&[ | ||
171 | "--from=markdown", | ||
172 | "--to=html", | ||
173 | "--css", | ||
174 | css_path | ||
175 | .as_ref() | ||
176 | .to_str() | ||
177 | .context("CSS path is not valid UTF-8")?, | ||
178 | "--standalone", | ||
179 | "--self-contained", | ||
180 | md_output_file | ||
181 | .to_str() | ||
182 | .context("Entity name is not valid UTF-8")?, | ||
183 | "--output", | ||
184 | html_output_file | ||
185 | .to_str() | ||
186 | .context("Entity name is not valid UTF-8")?, | ||
187 | ]); | ||
188 | |||
189 | let command_str = format!("{:?}", command); | ||
190 | debug!("Launching command: {}", command_str); | ||
191 | |||
192 | let output = command | ||
193 | .output() | ||
194 | .context("Failed to execute Pandoc command")?; | ||
195 | |||
196 | ensure!( | ||
197 | output.status.success(), | ||
198 | CommandError { | ||
199 | command: command_str, | ||
200 | status: output.status, | ||
201 | stderr: String::from_utf8(output.stderr).expect("Pandoc outputted invalid UTF-8"), | ||
202 | } | ||
203 | ); | ||
204 | |||
205 | Ok(leftovers) | ||
206 | } | ||
207 | |||
208 | #[derive(Debug, Clone, Error)] | ||
209 | #[error( | ||
210 | "Command {} returned status {:?} and stderr {:?}", | ||
211 | command, | ||
212 | status, | ||
213 | stderr | ||
214 | )] | ||
215 | struct CommandError { | ||
216 | command: String, | ||
217 | status: std::process::ExitStatus, | ||
218 | stderr: String, | ||
219 | } | ||