2020-06-04 16:39:33 -04:00
|
|
|
use quote::ToTokens;
|
|
|
|
use std::fs;
|
2020-07-12 12:28:42 -04:00
|
|
|
use std::io::Write;
|
2020-06-13 09:58:57 -04:00
|
|
|
use std::path::{Path, PathBuf};
|
2020-06-06 09:49:01 -04:00
|
|
|
use std::sync::Arc;
|
2020-07-14 09:07:35 -04:00
|
|
|
use syn::Block;
|
2020-06-04 16:39:33 -04:00
|
|
|
|
2020-06-05 22:58:14 -04:00
|
|
|
use crate::config::Config;
|
2020-06-04 16:39:33 -04:00
|
|
|
use crate::error::*;
|
|
|
|
use crate::optimizer::Optimizer;
|
|
|
|
use crate::parser::Parser;
|
|
|
|
use crate::resolver::Resolver;
|
2020-06-07 04:58:52 -04:00
|
|
|
use crate::translator::{TranslatedSource, Translator};
|
2020-06-06 12:25:52 -04:00
|
|
|
use crate::util::{read_to_string, rustfmt_block};
|
2020-06-04 16:39:33 -04:00
|
|
|
|
2020-06-13 09:58:57 -04:00
|
|
|
pub struct CompilationReport {
|
|
|
|
pub deps: Vec<PathBuf>,
|
|
|
|
}
|
|
|
|
|
2020-06-05 22:58:14 -04:00
|
|
|
#[derive(Default)]
|
2020-06-04 16:39:33 -04:00
|
|
|
pub struct Compiler {
|
2020-06-07 04:58:52 -04:00
|
|
|
config: Config,
|
2020-06-04 16:39:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Compiler {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self::default()
|
|
|
|
}
|
|
|
|
|
2020-06-05 22:58:14 -04:00
|
|
|
pub fn with_config(config: Config) -> Self {
|
|
|
|
Self { config }
|
2020-06-04 16:39:33 -04:00
|
|
|
}
|
|
|
|
|
2020-06-06 09:49:01 -04:00
|
|
|
fn translate_file_contents(&self, input: &Path) -> Result<TranslatedSource, Error> {
|
2020-06-05 22:58:14 -04:00
|
|
|
let parser = Parser::new().delimiter(self.config.delimiter);
|
|
|
|
let translator = Translator::new().escape(self.config.escape);
|
2020-06-06 12:25:52 -04:00
|
|
|
let content = read_to_string(input)
|
2020-06-06 09:49:01 -04:00
|
|
|
.chain_err(|| format!("Failed to open template file: {:?}", input))?;
|
|
|
|
|
|
|
|
let stream = parser.parse(&*content);
|
|
|
|
translator.translate(stream)
|
|
|
|
}
|
|
|
|
|
2020-06-07 04:58:52 -04:00
|
|
|
pub fn compile_file(
|
|
|
|
&self,
|
|
|
|
input: &Path,
|
|
|
|
output: &Path,
|
2020-06-13 09:58:57 -04:00
|
|
|
) -> Result<CompilationReport, Error> {
|
2020-06-06 09:49:01 -04:00
|
|
|
// TODO: introduce cache system
|
|
|
|
|
2020-06-14 04:30:36 -04:00
|
|
|
let input = input
|
|
|
|
.canonicalize()
|
|
|
|
.map_err(|_| format!("Template file not found: {:?}", input))?;
|
2020-06-06 10:43:41 -04:00
|
|
|
|
2020-06-06 11:43:10 -04:00
|
|
|
let include_handler = Arc::new(|child_file: &Path| -> Result<_, Error> {
|
|
|
|
Ok(self.translate_file_contents(&*child_file)?.ast)
|
2020-06-06 09:49:01 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
let resolver = Resolver::new().include_handler(include_handler);
|
2020-06-06 18:19:09 -04:00
|
|
|
let optimizer = Optimizer::new().rm_whitespace(self.config.rm_whitespace);
|
2020-06-04 16:39:33 -04:00
|
|
|
|
2020-06-14 04:30:36 -04:00
|
|
|
let compile_file = |input: &Path,
|
|
|
|
output: &Path|
|
|
|
|
-> Result<CompilationReport, Error> {
|
|
|
|
let mut tsource = self.translate_file_contents(input)?;
|
|
|
|
let mut report = CompilationReport { deps: Vec::new() };
|
|
|
|
|
2020-06-14 16:36:39 -04:00
|
|
|
let r = resolver.resolve(&*input, &mut tsource.ast)?;
|
2020-06-14 04:30:36 -04:00
|
|
|
report.deps = r.deps;
|
|
|
|
|
|
|
|
optimizer.optimize(&mut tsource.ast);
|
|
|
|
|
|
|
|
if let Some(parent) = output.parent() {
|
|
|
|
fs::create_dir_all(parent)
|
|
|
|
.chain_err(|| format!("Failed to save artifacts in {:?}", parent))?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let string = tsource.ast.into_token_stream().to_string();
|
2020-07-12 12:28:42 -04:00
|
|
|
|
|
|
|
let mut f = fs::File::create(output)
|
|
|
|
.chain_err(|| format!("Failed to create artifact: {:?}", output))?;
|
|
|
|
writeln!(f, "{}", rustfmt_block(&*string).unwrap_or(string))
|
|
|
|
.chain_err(|| format!("Failed to write artifact into {:?}", output))?;
|
2020-06-14 04:30:36 -04:00
|
|
|
Ok(report)
|
|
|
|
};
|
2020-06-04 16:39:33 -04:00
|
|
|
|
|
|
|
compile_file(&*input, &*output)
|
|
|
|
.chain_err(|| "Failed to compile template.")
|
|
|
|
.map_err(|mut e| {
|
|
|
|
e.source = fs::read_to_string(&*input).ok();
|
|
|
|
e.source_file = Some(input.to_owned());
|
|
|
|
e
|
2020-06-13 09:58:57 -04:00
|
|
|
})
|
2020-06-04 16:39:33 -04:00
|
|
|
}
|
2020-07-14 09:07:35 -04:00
|
|
|
|
|
|
|
pub fn compile_str(&self, input: &str) -> Result<String, Error> {
|
|
|
|
let dummy_path = Path::new(env!("CARGO_MANIFEST_DIR"));
|
|
|
|
|
|
|
|
let include_handler = Arc::new(|_: &Path| -> Result<Block, Error> {
|
|
|
|
Err(make_error!(
|
|
|
|
ErrorKind::AnalyzeError(
|
|
|
|
"include! macro is not allowed in inline template".to_owned()
|
|
|
|
),
|
|
|
|
source = input.to_owned()
|
|
|
|
))
|
|
|
|
});
|
|
|
|
|
|
|
|
let parser = Parser::new().delimiter(self.config.delimiter);
|
|
|
|
let translator = Translator::new().escape(self.config.escape);
|
|
|
|
let resolver = Resolver::new().include_handler(include_handler);
|
|
|
|
let optimizer = Optimizer::new().rm_whitespace(self.config.rm_whitespace);
|
|
|
|
|
|
|
|
let compile = || -> Result<String, Error> {
|
|
|
|
let stream = parser.parse(input);
|
|
|
|
let mut tsource = translator.translate(stream)?;
|
|
|
|
resolver.resolve(dummy_path, &mut tsource.ast)?;
|
|
|
|
|
|
|
|
optimizer.optimize(&mut tsource.ast);
|
|
|
|
Ok(tsource.ast.into_token_stream().to_string())
|
|
|
|
};
|
|
|
|
|
|
|
|
compile()
|
|
|
|
.chain_err(|| "Failed to compile template.")
|
|
|
|
.map_err(|mut e| {
|
|
|
|
e.source = Some(input.to_owned());
|
|
|
|
e
|
|
|
|
})
|
|
|
|
}
|
2020-06-04 16:39:33 -04:00
|
|
|
}
|