use crate::error::{Error, Result};
use proc_macro2::TokenStream;
pub(crate) fn test_vectors<TS>(args: TS, input: TS) -> TS
where
TokenStream: From<TS>,
TS: From<TokenStream>,
{
TS::from(
test_vectors_result(TokenStream::from(args), TokenStream::from(input))
.unwrap_or_else(Error::into_compile_error),
)
}
fn test_vectors_result(args: TokenStream, input: TokenStream) -> Result<TokenStream> {
use crate::fnargs::parse_fn_args;
use crate::listdir::list_dir;
use crate::params::MacroParams;
use quote::quote;
use syn::spanned::Spanned;
let spanargs = args.span();
let spaninput = input.span();
let params = MacroParams::parse(args)?;
let mut implfn: syn::ItemFn = syn::parse2(input)?;
let basename = implfn.sig.ident.to_string();
implfn.sig.ident = syn::Ident::new(&format!("impl_{}", &basename), implfn.sig.ident.span());
let implname = &implfn.sig.ident;
let tyret = &implfn.sig.output;
let (argnames, argtypes) =
parse_fn_args(&implfn.sig).map_err(|s| syn::Error::new(spaninput, s))?;
let casenames = list_dir(¶ms.dir).map_err(|e| syn::Error::new(spanargs, e.to_string()))?;
let mut casefns = vec![];
for casename in casenames {
let casefnname = syn::Ident::new(&format!("{}_{}", &basename, &casename), spanargs);
let argpaths = argnames
.iter()
.map(|arg| params.dir.join(&casename).join(arg).display().to_string());
let (testattr, execdoctest) = if params.doctest {
(
quote! {},
quote! {
{
eprintln!("doctest test {} ...", stringify!(#casefnname));
#casefnname ();
}
},
)
} else {
(quote! { #[test] }, quote! {})
};
casefns.push(quote! {
#testattr
fn #casefnname() #tyret {
#implname(
#(
<#argtypes>::try_from(
&include_bytes!( #argpaths )[..]
).unwrap()
),*
)
}
#execdoctest
});
}
Ok(quote! {
#implfn
#( #casefns )*
})
}