use proc_macro2::{TokenStream, Span};
use syn::{ItemFn, Block, Ident, };
use syn::parse::{Parse, ParseStream};
use syn;
use quote::{quote, ToTokens};
use core::iter::Extend;
const ERROR_STRS: [&str; 1] =
[
"expected function"
];
#[derive(Clone, Debug)]
pub struct WrappedFn
{
pub function: ItemFn,
pub pre_code: Option<TokenStream>,
pub post_code: Option<TokenStream>,
pub wrapper_ident: Ident,
pub result_ident: Ident
}
impl WrappedFn
{
pub fn set_pre_code(&mut self, pre_code: TokenStream)
{
self.pre_code = Some(pre_code);
}
pub fn set_post_code(&mut self, post_code: TokenStream)
{
self.post_code = Some(post_code);
}
pub fn remove_pre_code(&mut self)
{
self.pre_code = None;
}
pub fn remove_post_code(&mut self)
{
self.post_code = None;
}
pub fn set_wrapper_ident(&mut self, ident: &str)
{
self.wrapper_ident = Ident::new(ident, Span::call_site());
}
pub fn set_result_ident(&mut self, ident: &str)
{
self.result_ident = Ident::new(ident, Span::call_site());
}
fn add_unwrapped_code(function_block: &mut TokenStream, og_code: &Block)
{
function_block.extend(quote!{ #og_code });
}
fn add_wrapped_post_code(og_code: &Block, wrapper_ident: &Ident, result_ident: &Ident, function_block: &mut TokenStream, post_code: &TokenStream)
{
let wrapper_code = quote!
{
let mut #wrapper_ident = || #og_code ;
let #result_ident = #wrapper_ident ();
};
let return_line = quote!{ #result_ident };
function_block.extend(wrapper_code);
function_block.extend(post_code.clone());
function_block.extend(return_line);
}
fn get_wrapped_function(function: &ItemFn, function_block: &TokenStream) -> ItemFn
{
let function_block = quote!{ { #function_block } };
let mut new_function = function.clone();
new_function.block = syn::parse(function_block.into()).unwrap();
new_function
}
}
impl Parse for WrappedFn
{
fn parse(input: ParseStream) -> syn::Result<Self>
{
if input.is_empty()
{
return Err(syn::Error::new(input.span(), ERROR_STRS[0]))
}
let function: ItemFn = input.parse()?;
let wrapped_function = Self
{
function: function,
pre_code: None,
post_code: None,
wrapper_ident: Ident::new("wrapper", Span::call_site()),
result_ident: Ident::new("result", Span::call_site())
};
Ok(wrapped_function)
}
}
impl From<&WrappedFn> for ItemFn
{
fn from(function: &WrappedFn) -> Self
{
match (&function.pre_code, &function.post_code)
{
(Some(pre_code), Some(post_code)) =>
{
let mut function_block = pre_code.clone();
WrappedFn::add_wrapped_post_code(&function.function.block, &function.wrapper_ident, &function.result_ident, &mut function_block, post_code);
WrappedFn::get_wrapped_function(&function.function, &function_block)
},
(Some(pre_code), None) =>
{
let mut function_block = pre_code.clone();
WrappedFn::add_unwrapped_code(&mut function_block, &function.function.block);
WrappedFn::get_wrapped_function(&function.function, &function_block)
},
(None, Some(post_code)) =>
{
let mut function_block = TokenStream::new();
WrappedFn::add_wrapped_post_code(&function.function.block, &function.wrapper_ident, &function.result_ident, &mut function_block, post_code);
WrappedFn::get_wrapped_function(&function.function, &function_block)
},
(None, None) =>
{
function.function.clone()
}
}
}
}
impl From<WrappedFn> for ItemFn
{
fn from(function: WrappedFn) -> Self
{
Self::from(&function)
}
}
impl From<&WrappedFn> for TokenStream
{
fn from(function: &WrappedFn) -> Self
{
let function = ItemFn::from(function);
quote!{ #function }
}
}
impl From<WrappedFn> for TokenStream
{
fn from(function: WrappedFn) -> Self
{
let function = ItemFn::from(function);
quote!{ #function }
}
}
impl ToTokens for WrappedFn
{
fn to_tokens(&self, tokens: &mut TokenStream)
{
let function: TokenStream = self.into();
tokens.extend(function);
}
}