use proc_macro2::TokenStream;
use quote::quote;
use syn::{Ident, ItemTrait, parse_macro_input};
use crate::{
common::{self, __vmi_lifetime},
method::{FnArgExt, ItemExt, ItemFnExt},
transform,
};
struct TraitFn {
os_context_fn: TokenStream,
}
fn generate_impl_fns(item_fn: impl ItemFnExt) -> Option<TraitFn> {
let mut sig = item_fn.sig().clone();
transform::replace_self_with_os(&mut sig);
let ident = &sig.ident;
let generics = &sig.generics;
let return_type = &sig.output;
let (args, arg_names) = common::build_args(&sig)?;
let where_clause = common::build_where_clause(&sig);
let doc = item_fn.doc();
let os_context_fn = quote! {
#(#doc)*
pub fn #ident #generics(&self, #(#args),*) #return_type
#where_clause
{
Os::#ident(self.state(), #(#arg_names),*)
}
};
Some(TraitFn { os_context_fn })
}
fn transform_fn_to_trait_fn(item_fn: impl ItemFnExt) -> Option<TraitFn> {
let sig = item_fn.sig();
let mut inputs = sig.inputs.iter();
if !inputs.next()?.contains("VmiState") {
return None;
}
generate_impl_fns(item_fn)
}
pub fn derive_os_wrapper(
args: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let os_context_name = parse_macro_input!(args as Ident);
let input = parse_macro_input!(item as ItemTrait);
let trait_name = &input.ident;
let trait_methods = input
.items
.iter()
.filter_map(ItemExt::as_fn)
.filter_map(transform_fn_to_trait_fn);
let os_context_methods = trait_methods.clone().map(|m| m.os_context_fn);
let vmi_lifetime = __vmi_lifetime();
let expanded = quote! {
#input
impl<#vmi_lifetime, Os> #os_context_name<#vmi_lifetime, Os>
where
Os: #trait_name,
{
#(#os_context_methods)*
}
};
proc_macro::TokenStream::from(expanded)
}