extern crate proc_macro;
use proc_macro::{Ident, TokenStream, TokenTree};
#[proc_macro_attribute]
pub fn parameters(attr: TokenStream, function: TokenStream) -> TokenStream {
let mut tokens: Vec<TokenTree> = function.clone().into_iter().collect();
let func_name_idx = match validate_parameters_spec(&tokens) {
Ok(name) => name,
Err(e) => panic!("{}", e),
};
let (func_name, span) = (
tokens[func_name_idx].to_string(),
tokens[func_name_idx].span(),
);
let attr_list = attr.to_string();
let inner_func_name = format!("__{}", func_name);
tokens[func_name_idx] = TokenTree::Ident(Ident::new(&inner_func_name, span));
let test_runner_tokens = format!(
"[{attr_list}]
.into_iter()
.map({inner_func_name})
.collect::<Vec<extel::ExtelResult>>()"
);
let final_func = format!(
"{} {}() -> Vec<ExtelResult> {{ {} {} }}",
tokens[0..func_name_idx]
.iter()
.map(|token| token.to_string())
.collect::<Vec<_>>()
.join(" "),
func_name,
tokens.into_iter().collect::<TokenStream>(),
test_runner_tokens,
);
final_func.parse().unwrap()
}
fn validate_parameters_spec(tokens: &[TokenTree]) -> Result<usize, &'static str> {
let mut i: usize = 0;
while i < tokens.len() {
if let TokenTree::Ident(ident) = &tokens[i] {
match ident.to_string().as_str() {
"fn" => return Ok(i + 1),
"pub" => {}
_ => return Err("#[parameters(...)] can only be applied to functions"),
};
};
i += 1;
}
Err("reached end of token stream")
}