fix: Re-compile when child template is update.

This commit is contained in:
Kogia-sima 2020-06-13 22:58:57 +09:00
parent 9949c91f7d
commit 98e11b1163
3 changed files with 53 additions and 33 deletions

View File

@ -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(())
})
}
}

View File

@ -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 _;

View File

@ -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 })
}
}
}