use proc_macro2::TokenStream as TokenStream2;
use quote::ToTokens;
use syn;
use syn::fold::Fold;
use super::codegen::Codegen;
use super::config::Config;
#[derive(PartialOrd, PartialEq, Debug)]
enum FoldScope {
Fn = 0,
Impl = 1,
Mod = 2,
}
#[derive(Debug)]
pub struct Folder {
scope: FoldScope,
current_impl: Option<syn::Type>,
}
impl Folder {
fn new(scope: FoldScope) -> Self {
Self {
scope,
current_impl: None,
}
}
}
impl Folder {
pub fn fold(args: TokenStream2, input: TokenStream2) -> TokenStream2 {
let config = syn::parse2::<Config>(args).expect("Failed to parse attribute configurations");
if config.ignore {
return input;
}
let body = syn::parse2::<syn::ItemMod>(input.clone());
if let Ok(body) = body {
return Folder::new(FoldScope::Mod)
.fold_item_mod(body)
.into_token_stream();
}
let body = syn::parse2::<syn::ItemFn>(input.clone());
if let Ok(body) = body {
return Folder::new(FoldScope::Fn)
.fold_item_fn(body)
.into_token_stream();
}
let body = syn::parse2::<syn::ItemImpl>(input.clone());
if let Ok(body) = body {
return Folder::new(FoldScope::Impl)
.fold_item_impl(body)
.into_token_stream();
}
panic!("Invalid attribute position, only supports function, impl and mod.");
}
fn extract_macro_config(attributes: &[syn::Attribute]) -> Option<Config> {
for attr in attributes {
let some_last = attr.path.segments.iter().last();
if let Some(last) = some_last {
if last.ident.to_string() == "trace2" {
let config = syn::parse2::<AttrTTS>(attr.tts.clone())
.expect("Failed to parse attribute configurations");
return Some(config.0);
}
}
}
None
}
}
impl Fold for Folder {
fn fold_impl_item_method(&mut self, mut i: syn::ImplItemMethod) -> syn::ImplItemMethod {
let some_config = Self::extract_macro_config(&i.attrs);
if let Some(_) = some_config {
if self.scope > FoldScope::Fn {
return i;
}
}
let new_block_tokens = Codegen::build_block(&i.sig.decl, &i.sig.ident, self.current_impl.as_ref(), &i.block);
let new_block = syn::parse2(new_block_tokens).unwrap();
i.block = new_block;
i
}
fn fold_item_fn(&mut self, mut i: syn::ItemFn) -> syn::ItemFn {
let some_config = Self::extract_macro_config(&i.attrs);
if let Some(_) = some_config {
if self.scope > FoldScope::Fn {
return i;
}
}
let new_block_tokens = Codegen::build_block(&*i.decl, &i.ident, None, &*i.block);
let new_block = syn::parse2(new_block_tokens).unwrap();
i.block = Box::new(new_block);
i
}
fn fold_item_impl(&mut self, i: syn::ItemImpl) -> syn::ItemImpl {
let some_config = Self::extract_macro_config(&i.attrs);
if let Some(_) = some_config {
if self.scope > FoldScope::Impl {
return i;
}
}
self.current_impl = Some(TypeFolder.fold_type(*(i.self_ty).clone()));
let ret = syn::fold::fold_item_impl(self, i);
self.current_impl = None;
ret
}
}
struct TypeFolder;
impl Fold for TypeFolder {
fn fold_path(&mut self, mut i: syn::Path) -> syn::Path {
let segments_len = i.segments.len();
if segments_len > 1 {
i.segments = i.segments.into_iter().skip(segments_len - 1).collect();
}
i
}
}
#[derive(Debug)]
struct AttrTTS(Config);
impl syn::synom::Synom for AttrTTS {
named!(parse -> Self, do_parse!(
config: option!(parens!(syn!(Config))) >>
(AttrTTS(config.map(|pair| pair.1).unwrap_or_default()))
));
}
#[cfg(test)]
mod test {
use super::AttrTTS;
use syn;
#[test]
fn parse_attr_tts() {
let attr_tts = syn::parse_str::<AttrTTS>("").unwrap();
assert!(attr_tts.0.ignore, false);
let attr_tts = syn::parse_str::<AttrTTS>("(ignore)").unwrap();
assert_eq!(attr_tts.0.ignore, true);
}
}