2020-06-06 15:53:49 -04:00
|
|
|
use quote::quote;
|
2020-06-06 11:43:10 -04:00
|
|
|
use std::path::{Path, PathBuf};
|
2020-06-06 09:49:01 -04:00
|
|
|
use std::sync::Arc;
|
|
|
|
use syn::visit_mut::VisitMut;
|
2020-06-11 08:35:14 -04:00
|
|
|
use syn::{Block, Expr, ExprBlock, ExprMacro, LitStr};
|
2020-06-04 16:39:33 -04:00
|
|
|
|
|
|
|
use crate::error::*;
|
|
|
|
|
2020-06-06 09:49:01 -04:00
|
|
|
macro_rules! matches_or_else {
|
|
|
|
($val:expr, $p:pat, $ok:expr, $else:expr) => {
|
|
|
|
match $val {
|
|
|
|
$p => $ok,
|
|
|
|
_ => $else,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2020-06-04 16:39:33 -04:00
|
|
|
|
2020-06-06 09:49:01 -04:00
|
|
|
macro_rules! return_if_some {
|
|
|
|
($val:expr) => {
|
|
|
|
if $val.is_some() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-12-18 09:00:40 -05:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct Resolver<'h> {
|
|
|
|
include_handler: Arc<dyn 'h + Fn(&Path) -> Result<Block, Error>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'h> Resolver<'h> {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
include_handler: Arc::new(|_| {
|
|
|
|
Err(make_error!(ErrorKind::AnalyzeError(
|
|
|
|
"You cannot use `include` macro inside templates".to_owned()
|
|
|
|
)))
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn include_handler(
|
|
|
|
mut self,
|
|
|
|
new: Arc<dyn 'h + Fn(&Path) -> Result<Block, Error>>,
|
|
|
|
) -> Resolver<'h> {
|
|
|
|
self.include_handler = new;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn resolve(
|
|
|
|
&self,
|
|
|
|
input_file: &Path,
|
|
|
|
ast: &mut Block,
|
|
|
|
) -> Result<ResolveReport, Error> {
|
|
|
|
let mut child = ResolverImpl {
|
|
|
|
path_stack: vec![input_file.to_owned()],
|
|
|
|
deps: Vec::new(),
|
|
|
|
error: None,
|
|
|
|
include_handler: Arc::clone(&self.include_handler),
|
|
|
|
};
|
|
|
|
child.visit_block_mut(ast);
|
|
|
|
|
|
|
|
if let Some(e) = child.error {
|
|
|
|
Err(e)
|
|
|
|
} else {
|
|
|
|
Ok(ResolveReport { deps: child.deps })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-13 09:58:57 -04:00
|
|
|
pub struct ResolveReport {
|
|
|
|
pub deps: Vec<PathBuf>,
|
|
|
|
}
|
|
|
|
|
2020-06-14 16:36:39 -04:00
|
|
|
struct ResolverImpl<'h> {
|
2020-06-06 11:43:10 -04:00
|
|
|
path_stack: Vec<PathBuf>,
|
2020-06-13 09:58:57 -04:00
|
|
|
deps: Vec<PathBuf>,
|
2020-06-06 09:49:01 -04:00
|
|
|
error: Option<Error>,
|
2020-06-06 11:43:10 -04:00
|
|
|
include_handler: Arc<dyn 'h + Fn(&Path) -> Result<Block, Error>>,
|
2020-06-06 09:49:01 -04:00
|
|
|
}
|
|
|
|
|
2020-06-14 16:36:39 -04:00
|
|
|
impl<'h> ResolverImpl<'h> {
|
2020-06-11 08:35:14 -04:00
|
|
|
fn resolve_include(&mut self, i: &ExprMacro) -> Result<Expr, Error> {
|
|
|
|
let arg = match syn::parse2::<LitStr>(i.mac.tokens.clone()) {
|
2020-06-06 09:49:01 -04:00
|
|
|
Ok(l) => l.value(),
|
|
|
|
Err(e) => {
|
|
|
|
let mut e = Error::from(e);
|
|
|
|
e.chains.push(ErrorKind::AnalyzeError(
|
|
|
|
"invalid arguments for `include` macro".to_owned(),
|
|
|
|
));
|
2020-06-11 08:35:14 -04:00
|
|
|
return Err(e);
|
2020-06-06 09:49:01 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-06-06 15:53:49 -04:00
|
|
|
// resolve include! for rust file
|
2020-06-06 11:52:53 -04:00
|
|
|
if arg.ends_with(".rs") {
|
2020-06-14 16:36:39 -04:00
|
|
|
let absolute_path = if Path::new(&*arg).is_absolute() {
|
|
|
|
PathBuf::from(&arg[1..])
|
2020-06-11 08:35:14 -04:00
|
|
|
} else {
|
|
|
|
self.path_stack.last().unwrap().parent().unwrap().join(arg)
|
|
|
|
};
|
2020-12-16 05:13:37 -05:00
|
|
|
|
|
|
|
return if let Some(absolute_path_str) = absolute_path.to_str() {
|
|
|
|
Ok(syn::parse2(quote! { include!(#absolute_path_str) }).unwrap())
|
|
|
|
} else {
|
|
|
|
let msg = format!(
|
|
|
|
"cannot include path with non UTF-8 character: {:?}",
|
|
|
|
absolute_path
|
|
|
|
);
|
|
|
|
Err(make_error!(ErrorKind::AnalyzeError(msg)))
|
|
|
|
};
|
2020-06-06 11:52:53 -04:00
|
|
|
}
|
|
|
|
|
2020-06-06 11:43:10 -04:00
|
|
|
// resolve the template file path
|
2020-06-06 16:51:54 -04:00
|
|
|
// TODO: How should arguments be interpreted on Windows?
|
2020-06-14 16:36:39 -04:00
|
|
|
let child_template_file = if Path::new(&*arg).is_absolute() {
|
2020-06-06 11:43:10 -04:00
|
|
|
// absolute imclude
|
2020-06-14 16:36:39 -04:00
|
|
|
PathBuf::from(&arg[1..])
|
2020-06-06 11:43:10 -04:00
|
|
|
} else {
|
|
|
|
// relative include
|
2020-12-18 05:18:52 -05:00
|
|
|
self.path_stack.last().unwrap().parent().unwrap().join(arg)
|
2020-06-06 11:43:10 -04:00
|
|
|
};
|
|
|
|
|
2020-06-06 09:49:01 -04:00
|
|
|
// parse and translate the child template
|
2020-06-13 09:58:57 -04:00
|
|
|
let mut blk = (*self.include_handler)(&*child_template_file).chain_err(|| {
|
|
|
|
format!("Failed to include {:?}", child_template_file.clone())
|
|
|
|
})?;
|
2020-06-06 09:49:01 -04:00
|
|
|
|
2020-06-13 09:58:57 -04:00
|
|
|
self.path_stack.push(child_template_file);
|
2020-06-06 09:49:01 -04:00
|
|
|
syn::visit_mut::visit_block_mut(self, &mut blk);
|
2020-06-13 09:58:57 -04:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2020-06-06 09:49:01 -04:00
|
|
|
|
2020-06-11 08:35:14 -04:00
|
|
|
Ok(Expr::Block(ExprBlock {
|
2020-06-06 09:49:01 -04:00
|
|
|
attrs: Vec::new(),
|
|
|
|
label: None,
|
|
|
|
block: blk,
|
2020-06-11 08:35:14 -04:00
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-14 16:36:39 -04:00
|
|
|
impl<'h> VisitMut for ResolverImpl<'h> {
|
2020-06-11 08:35:14 -04:00
|
|
|
fn visit_expr_mut(&mut self, i: &mut Expr) {
|
|
|
|
return_if_some!(self.error);
|
|
|
|
let em = matches_or_else!(*i, Expr::Macro(ref mut em), em, {
|
|
|
|
syn::visit_mut::visit_expr_mut(self, i);
|
|
|
|
return;
|
2020-06-06 09:49:01 -04:00
|
|
|
});
|
2020-06-11 08:35:14 -04:00
|
|
|
|
|
|
|
// resolve `include`
|
|
|
|
if em.mac.path.is_ident("include") {
|
|
|
|
match self.resolve_include(em) {
|
|
|
|
Ok(e) => *i = e,
|
|
|
|
Err(e) => {
|
|
|
|
self.error = Some(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-06 09:49:01 -04:00
|
|
|
}
|
|
|
|
}
|