sailfish_compiler/
compiler.rs1use quote::ToTokens;
2use std::fs;
3use std::io::Write;
4use std::path::{Path, PathBuf};
5use std::sync::Arc;
6use syn::Block;
7
8use crate::analyzer::Analyzer;
9use crate::config::Config;
10use crate::error::*;
11use crate::optimizer::Optimizer;
12use crate::parser::Parser;
13use crate::resolver::Resolver;
14use crate::translator::{TranslatedSource, Translator};
15use crate::util::{read_to_string, rustfmt_block};
16
17#[derive(Default)]
18pub struct Compiler {
19 config: Config,
20}
21
22pub struct CompilationReport {
23 pub deps: Vec<PathBuf>,
24}
25
26impl Compiler {
27 pub fn new() -> Self {
28 Self::default()
29 }
30
31 pub fn with_config(config: Config) -> Self {
32 Self { config }
33 }
34
35 fn translate_file_contents(&self, input: &Path) -> Result<TranslatedSource, Error> {
36 let parser = Parser::new().delimiter(self.config.delimiter);
37 let translator = Translator::new().escape(self.config.escape);
38 let content = read_to_string(input)
39 .chain_err(|| format!("Failed to open template file: {:?}", input))?;
40
41 let stream = parser.parse(&content);
42 translator.translate(stream)
43 }
44
45 pub fn resolve_file(
46 &self,
47 input: &Path,
48 ) -> Result<(TranslatedSource, CompilationReport), Error> {
49 let include_handler = Arc::new(|child_file: &Path| -> Result<_, Error> {
50 Ok(self.translate_file_contents(child_file)?.ast)
51 });
52
53 let resolver = Resolver::new().include_handler(include_handler);
54 let mut tsource = self.translate_file_contents(input)?;
55 let mut report = CompilationReport { deps: Vec::new() };
56
57 let r = resolver.resolve(input, &mut tsource.ast)?;
58 report.deps = r.deps;
59 Ok((tsource, report))
60 }
61
62 pub fn compile_file(
63 &self,
64 input: &Path,
65 tsource: TranslatedSource,
66 output: &Path,
67 ) -> Result<(), Error> {
68 let analyzer = Analyzer::new();
69 let optimizer = Optimizer::new()
70 .rm_whitespace(self.config.rm_whitespace)
71 .rm_newline(self.config.rm_newline);
72
73 let compile_file = |mut tsource: TranslatedSource,
74 output: &Path|
75 -> Result<(), Error> {
76 analyzer.analyze(&mut tsource.ast)?;
77 optimizer.optimize(&mut tsource.ast);
78
79 if let Some(parent) = output.parent() {
80 fs::create_dir_all(parent)
81 .chain_err(|| format!("Failed to save artifacts in {:?}", parent))?;
82 }
83
84 let string = tsource.ast.into_token_stream().to_string();
85
86 let mut f = fs::File::create(output)
87 .chain_err(|| format!("Failed to create artifact: {:?}", output))?;
88 writeln!(f, "// Template compiled from: {}", input.display())
89 .chain_err(|| format!("Failed to write artifact into {:?}", output))?;
90 writeln!(f, "{}", rustfmt_block(&string).unwrap_or(string))
91 .chain_err(|| format!("Failed to write artifact into {:?}", output))?;
92 drop(f);
93
94 Ok(())
95 };
96
97 compile_file(tsource, output)
98 .chain_err(|| "Failed to compile template.")
99 .map_err(|mut e| {
100 e.source = fs::read_to_string(input).ok();
101 e.source_file = Some(input.to_owned());
102 e
103 })
104 }
105
106 pub fn compile_str(&self, input: &str) -> Result<String, Error> {
107 let dummy_path = Path::new(env!("CARGO_MANIFEST_DIR"));
108
109 let include_handler = Arc::new(|_: &Path| -> Result<Block, Error> {
110 Err(make_error!(
111 ErrorKind::AnalyzeError(
112 "include! macro is not allowed in inline template".to_owned()
113 ),
114 source = input.to_owned()
115 ))
116 });
117
118 let parser = Parser::new().delimiter(self.config.delimiter);
119 let translator = Translator::new().escape(self.config.escape);
120 let resolver = Resolver::new().include_handler(include_handler);
121 let optimizer = Optimizer::new()
122 .rm_whitespace(self.config.rm_whitespace)
123 .rm_newline(self.config.rm_newline);
124
125 let compile = || -> Result<String, Error> {
126 let stream = parser.parse(input);
127 let mut tsource = translator.translate(stream)?;
128 resolver.resolve(dummy_path, &mut tsource.ast)?;
129
130 optimizer.optimize(&mut tsource.ast);
131 Ok(tsource.ast.into_token_stream().to_string())
132 };
133
134 compile()
135 .chain_err(|| "Failed to compile template.")
136 .map_err(|mut e| {
137 e.source = Some(input.to_owned());
138 e
139 })
140 }
141}