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