Implement include! macro
This commit is contained in:
parent
188e90cfc3
commit
12174ff5c5
|
@ -1,13 +1,14 @@
|
|||
use quote::ToTokens;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::error::*;
|
||||
use crate::optimizer::Optimizer;
|
||||
use crate::parser::Parser;
|
||||
use crate::resolver::Resolver;
|
||||
use crate::translator::Translator;
|
||||
use crate::translator::{Translator, TranslatedSource};
|
||||
use crate::util::rustfmt_block;
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -24,21 +25,35 @@ impl Compiler {
|
|||
Self { config }
|
||||
}
|
||||
|
||||
pub fn compile_file(&self, input: &Path, output: &Path) -> Result<(), Error> {
|
||||
// TODO: introduce cache system
|
||||
|
||||
fn translate_file_contents(&self, input: &Path) -> Result<TranslatedSource, Error> {
|
||||
let parser = Parser::new().delimiter(self.config.delimiter);
|
||||
let translator = Translator::new().escape(self.config.escape);
|
||||
let resolver = Resolver::new();
|
||||
let content = fs::read_to_string(input)
|
||||
.chain_err(|| format!("Failed to open template file: {:?}", input))?;
|
||||
|
||||
let stream = parser.parse(&*content);
|
||||
translator.translate(stream)
|
||||
}
|
||||
|
||||
pub fn compile_file(&self, template_dir: &Path, input: &Path, output: &Path) -> Result<(), Error> {
|
||||
// TODO: introduce cache system
|
||||
|
||||
let input = if input.is_absolute() {
|
||||
input.to_owned()
|
||||
} else {
|
||||
template_dir.join(input)
|
||||
};
|
||||
|
||||
let include_handler = Arc::new(|arg: &str| -> Result<_, Error> {
|
||||
let input_file = template_dir.join(arg);
|
||||
Ok(self.translate_file_contents(&*input_file)?.ast)
|
||||
});
|
||||
|
||||
let resolver = Resolver::new().include_handler(include_handler);
|
||||
let optimizer = Optimizer::new();
|
||||
|
||||
let compile_file = |input: &Path, output: &Path| -> Result<(), Error> {
|
||||
let content = fs::read_to_string(&*input)
|
||||
.chain_err(|| format!("Failed to open template file: {:?}", input))?;
|
||||
|
||||
let stream = parser.parse(&*content);
|
||||
let mut tsource = translator.translate(stream)?;
|
||||
drop(content);
|
||||
let mut tsource = self.translate_file_contents(input)?;
|
||||
|
||||
resolver.resolve(&mut tsource.ast)?;
|
||||
optimizer.optimize(&mut tsource.ast);
|
||||
|
|
|
@ -113,6 +113,7 @@ struct TemplateStruct {
|
|||
}
|
||||
|
||||
fn compile(
|
||||
template_dir: &Path,
|
||||
input_file: &Path,
|
||||
output_file: &Path,
|
||||
options: &DeriveTemplateOptions,
|
||||
|
@ -126,7 +127,7 @@ fn compile(
|
|||
}
|
||||
|
||||
let compiler = Compiler::with_config(config);
|
||||
compiler.compile_file(input_file, &*output_file)
|
||||
compiler.compile_file(template_dir, input_file, &*output_file)
|
||||
}
|
||||
|
||||
fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error> {
|
||||
|
@ -138,15 +139,13 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
|
|||
all_options.merge(opt)?;
|
||||
}
|
||||
|
||||
let mut template_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect(
|
||||
"Internal error: environmental variable `CARGO_MANIFEST_DIR` is not set.",
|
||||
));
|
||||
template_dir.push("templates");
|
||||
|
||||
let input_file = match all_options.path {
|
||||
Some(ref path) => {
|
||||
let mut input = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect(
|
||||
"Internal error: environmental variable `CARGO_MANIFEST_DIR` is not set.",
|
||||
));
|
||||
input.push("templates");
|
||||
input.push(path.value());
|
||||
input
|
||||
}
|
||||
Some(ref path) => template_dir.join(path.value()),
|
||||
None => {
|
||||
return Err(syn::Error::new(
|
||||
Span::call_site(),
|
||||
|
@ -178,7 +177,7 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
|
|||
output_file.push("templates");
|
||||
output_file.push(filename);
|
||||
|
||||
compile(&*input_file, &*output_file, &all_options)
|
||||
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();
|
||||
|
|
|
@ -1,19 +1,127 @@
|
|||
use syn::Block;
|
||||
use std::sync::Arc;
|
||||
use syn::visit_mut::VisitMut;
|
||||
use syn::{Block, Expr, ExprBlock, LitStr};
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Resolver {}
|
||||
macro_rules! matches_or_else {
|
||||
($val:expr, $p:pat, $ok:expr, $else:expr) => {
|
||||
match $val {
|
||||
$p => $ok,
|
||||
_ => $else,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Resolver {
|
||||
#[inline]
|
||||
macro_rules! return_if_some {
|
||||
($val:expr) => {
|
||||
if $val.is_some() {
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn empty_block() -> Block {
|
||||
Block {
|
||||
brace_token: Default::default(),
|
||||
stmts: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
struct ResolverImpl<'h> {
|
||||
deps: Vec<String>,
|
||||
error: Option<Error>,
|
||||
include_handler: Arc<dyn 'h + Fn(&str) -> Result<Block, Error>>,
|
||||
}
|
||||
|
||||
impl<'h> VisitMut for ResolverImpl<'h> {
|
||||
fn visit_expr_mut(&mut self, i: &mut Expr) {
|
||||
return_if_some!(self.error);
|
||||
let em = matches_or_else!(*i, Expr::Macro(ref em), em, {
|
||||
syn::visit_mut::visit_expr_mut(self, i);
|
||||
return;
|
||||
});
|
||||
|
||||
// check if path is `include`
|
||||
if !em.mac.path.is_ident("include") {
|
||||
syn::visit_mut::visit_expr_mut(self, i);
|
||||
return;
|
||||
}
|
||||
|
||||
let arg = match syn::parse2::<LitStr>(em.mac.tokens.clone()) {
|
||||
Ok(l) => l.value(),
|
||||
Err(e) => {
|
||||
let mut e = Error::from(e);
|
||||
e.chains.push(ErrorKind::AnalyzeError(
|
||||
"invalid arguments for `include` macro".to_owned(),
|
||||
));
|
||||
self.error = Some(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: support relative path
|
||||
if arg.starts_with("./") || arg.starts_with("../") {
|
||||
self.error = Some(make_error!(ErrorKind::Unimplemented(
|
||||
"include! with relative path is not supported yet.".to_owned()
|
||||
)))
|
||||
}
|
||||
|
||||
// parse and translate the child template
|
||||
let mut blk = match (*self.include_handler)(&arg) {
|
||||
Ok(blk) => blk,
|
||||
Err(mut e) => {
|
||||
e.chains
|
||||
.push(ErrorKind::Other(format!("Failed to include {}", arg)));
|
||||
self.error = Some(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.deps.push(arg);
|
||||
syn::visit_mut::visit_block_mut(self, &mut blk);
|
||||
|
||||
*i = Expr::Block(ExprBlock {
|
||||
attrs: Vec::new(),
|
||||
label: None,
|
||||
block: blk,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Resolver<'h> {
|
||||
include_handler: Arc<dyn 'h + Fn(&str) -> Result<Block, Error>>,
|
||||
}
|
||||
|
||||
impl<'h> Resolver<'h> {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
Self {
|
||||
include_handler: Arc::new(|_| {
|
||||
Err(make_error!(ErrorKind::AnalyzeError(
|
||||
"You cannot use `include` macro inside templates".to_owned()
|
||||
)))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn resolve(&self, _ast: &mut Block) -> Result<(), Error> {
|
||||
// not implemented yet
|
||||
pub fn include_handler(
|
||||
self,
|
||||
new: Arc<dyn 'h + Fn(&str) -> Result<Block, Error>>,
|
||||
) -> Resolver<'h> {
|
||||
Self {
|
||||
include_handler: new,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn resolve(&self, ast: &mut Block) -> Result<(), Error> {
|
||||
ResolverImpl {
|
||||
deps: Vec::new(),
|
||||
error: None,
|
||||
include_handler: Arc::clone(&self.include_handler)
|
||||
}.visit_block_mut(ast);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue