fix: Re-compile when child template is update.
This commit is contained in:
parent
9949c91f7d
commit
98e11b1163
|
@ -1,6 +1,6 @@
|
|||
use quote::ToTokens;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::config::Config;
|
||||
|
@ -11,6 +11,10 @@ use crate::resolver::Resolver;
|
|||
use crate::translator::{TranslatedSource, Translator};
|
||||
use crate::util::{read_to_string, rustfmt_block};
|
||||
|
||||
pub struct CompilationReport {
|
||||
pub deps: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Compiler {
|
||||
config: Config,
|
||||
|
@ -40,7 +44,7 @@ impl Compiler {
|
|||
template_dir: &Path,
|
||||
input: &Path,
|
||||
output: &Path,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<CompilationReport, Error> {
|
||||
// TODO: introduce cache system
|
||||
|
||||
let input = input.canonicalize()?;
|
||||
|
@ -52,23 +56,27 @@ impl Compiler {
|
|||
let resolver = Resolver::new().include_handler(include_handler);
|
||||
let optimizer = Optimizer::new().rm_whitespace(self.config.rm_whitespace);
|
||||
|
||||
let compile_file = |input: &Path, output: &Path| -> Result<(), Error> {
|
||||
let mut tsource = self.translate_file_contents(input)?;
|
||||
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() };
|
||||
|
||||
resolver.resolve(template_dir, &*input, &mut tsource.ast)?;
|
||||
optimizer.optimize(&mut tsource.ast);
|
||||
let r = resolver.resolve(template_dir, &*input, &mut tsource.ast)?;
|
||||
report.deps = r.deps;
|
||||
|
||||
if let Some(parent) = output.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
if output.exists() {
|
||||
fs::remove_file(output)?;
|
||||
}
|
||||
optimizer.optimize(&mut tsource.ast);
|
||||
|
||||
let string = tsource.ast.into_token_stream().to_string();
|
||||
fs::write(output, rustfmt_block(&*string).unwrap_or(string))?;
|
||||
Ok(())
|
||||
};
|
||||
if let Some(parent) = output.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
if output.exists() {
|
||||
fs::remove_file(output)?;
|
||||
}
|
||||
|
||||
let string = tsource.ast.into_token_stream().to_string();
|
||||
fs::write(output, rustfmt_block(&*string).unwrap_or(string))?;
|
||||
Ok(report)
|
||||
};
|
||||
|
||||
compile_file(&*input, &*output)
|
||||
.chain_err(|| "Failed to compile template.")
|
||||
|
@ -76,8 +84,6 @@ impl Compiler {
|
|||
e.source = fs::read_to_string(&*input).ok();
|
||||
e.source_file = Some(input.to_owned());
|
||||
e
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,11 @@ use syn::parse::{Parse, ParseStream, Result as ParseResult};
|
|||
use syn::punctuated::Punctuated;
|
||||
use syn::{Fields, Ident, ItemStruct, LitBool, LitChar, LitStr, Token};
|
||||
|
||||
use crate::compiler::Compiler;
|
||||
use crate::compiler::{CompilationReport, Compiler};
|
||||
use crate::config::Config;
|
||||
use crate::error::*;
|
||||
|
||||
// arguments for include_template* macros
|
||||
// options for `template` attributes
|
||||
#[derive(Default)]
|
||||
struct DeriveTemplateOptions {
|
||||
path: Option<LitStr>,
|
||||
|
@ -103,7 +103,7 @@ fn compile(
|
|||
input_file: &Path,
|
||||
output_file: &Path,
|
||||
options: &DeriveTemplateOptions,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<CompilationReport, Error> {
|
||||
let mut config = Config::default();
|
||||
if let Some(ref delimiter) = options.delimiter {
|
||||
config.delimiter = delimiter.value();
|
||||
|
@ -177,12 +177,18 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
|
|||
output_file.push("templates");
|
||||
output_file.push(filename);
|
||||
|
||||
compile(&*template_dir, &*input_file, &*output_file, &all_options)
|
||||
let report = compile(&*template_dir, &*input_file, &*output_file, &all_options)
|
||||
.map_err(|e| syn::Error::new(Span::call_site(), e))?;
|
||||
|
||||
let input_file_string = input_file.to_string_lossy();
|
||||
let output_file_string = output_file.to_string_lossy();
|
||||
|
||||
let mut include_bytes_seq = quote! { include_bytes!(#input_file_string); };
|
||||
for dep in report.deps {
|
||||
let dep_string = dep.to_string_lossy();
|
||||
include_bytes_seq.extend(quote! { include_bytes!(#dep_string); });
|
||||
}
|
||||
|
||||
// Generate tokens
|
||||
|
||||
let name = strct.ident;
|
||||
|
@ -211,7 +217,7 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
|
|||
let tokens = quote! {
|
||||
impl #impl_generics sailfish::TemplateOnce for #name #ty_generics #where_clause {
|
||||
fn render_once(self) -> sailfish::runtime::RenderResult {
|
||||
include_bytes!(#input_file_string);
|
||||
#include_bytes_seq;
|
||||
|
||||
use sailfish::runtime as sfrt;
|
||||
use sfrt::RenderInternal as _;
|
||||
|
|
|
@ -23,10 +23,14 @@ macro_rules! return_if_some {
|
|||
};
|
||||
}
|
||||
|
||||
pub struct ResolveReport {
|
||||
pub deps: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
struct ResolverImpl<'s, 'h> {
|
||||
template_dir: &'s Path,
|
||||
path_stack: Vec<PathBuf>,
|
||||
deps: Vec<String>,
|
||||
deps: Vec<PathBuf>,
|
||||
error: Option<Error>,
|
||||
include_handler: Arc<dyn 'h + Fn(&Path) -> Result<Block, Error>>,
|
||||
}
|
||||
|
@ -57,7 +61,7 @@ impl<'s, 'h> ResolverImpl<'s, 'h> {
|
|||
|
||||
// resolve the template file path
|
||||
// TODO: How should arguments be interpreted on Windows?
|
||||
let input_file = if arg.starts_with('/') {
|
||||
let child_template_file = if arg.starts_with('/') {
|
||||
// absolute imclude
|
||||
self.template_dir.join(&arg[1..])
|
||||
} else {
|
||||
|
@ -71,13 +75,17 @@ impl<'s, 'h> ResolverImpl<'s, 'h> {
|
|||
};
|
||||
|
||||
// parse and translate the child template
|
||||
let mut blk = (*self.include_handler)(&*input_file)
|
||||
.chain_err(|| format!("Failed to include {:?}", input_file))?;
|
||||
let mut blk = (*self.include_handler)(&*child_template_file).chain_err(|| {
|
||||
format!("Failed to include {:?}", child_template_file.clone())
|
||||
})?;
|
||||
|
||||
self.path_stack.push(input_file);
|
||||
self.deps.push(arg);
|
||||
self.path_stack.push(child_template_file);
|
||||
syn::visit_mut::visit_block_mut(self, &mut blk);
|
||||
self.path_stack.pop();
|
||||
|
||||
let child_template_file = self.path_stack.pop().unwrap();
|
||||
if self.deps.iter().all(|p| p != &child_template_file) {
|
||||
self.deps.push(child_template_file);
|
||||
}
|
||||
|
||||
Ok(Expr::Block(ExprBlock {
|
||||
attrs: Vec::new(),
|
||||
|
@ -140,7 +148,7 @@ impl<'h> Resolver<'h> {
|
|||
template_dir: &Path,
|
||||
input_file: &Path,
|
||||
ast: &mut Block,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<ResolveReport, Error> {
|
||||
let mut child = ResolverImpl {
|
||||
template_dir,
|
||||
path_stack: vec![input_file.to_owned()],
|
||||
|
@ -153,7 +161,7 @@ impl<'h> Resolver<'h> {
|
|||
if let Some(e) = child.error {
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(())
|
||||
Ok(ResolveReport { deps: child.deps })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue