use proc_macro::TokenStream;
use syn::{FnArg, ItemFn, Receiver};
use quote::quote;
pub fn assert_function_consumes_impl(whitelist: &[String], function: ItemFn) -> TokenStream {
let mut errors = Vec::new();
for w in whitelist {
let mut found_match = false;
for input_arg in &function.sig.inputs {
match input_arg {
FnArg::Typed(arg) => {
let arg_type = &arg.ty;
let arg_type_str = quote! { #arg_type }.to_string();
if is_type_compatible(&arg_type_str, w) {
found_match = true;
break;
}
}
FnArg::Receiver(receiver) => {
if is_receiver_compatible(receiver, w) {
found_match = true;
break;
}
}
}
}
if !found_match {
errors.push(syn::Error::new(
function.sig.ident.span(),
format!("Consumes-macro error: `{}` type is not consumed by the `{}` function", w, function.sig.ident),
));
}
}
if !errors.is_empty() {
let error_messages: Vec<String> = errors.into_iter().map(|e| e.to_string()).collect();
let error_message = error_messages.join("\n");
return TokenStream::from(quote! {
compile_error!(#error_message);
});
}
TokenStream::from(quote! { #function })
}
fn is_receiver_compatible(receiver: &Receiver, whitelist_type: &str) -> bool {
let is_ref = receiver.reference.is_some();
let is_mut = receiver.mutability.is_some();
match (is_ref, is_mut, whitelist_type) {
(false, false, "self") => true, (false, true, "mut self") => true, (true, false, "&self") => true, (true, true, "&mut self") => true, _ => false,
}
}
fn is_type_compatible(arg_type_str: &str, whitelist_type: &str) -> bool {
arg_type_str.trim() == whitelist_type
}