extern crate proc_macro;
use anchor_syn::parser;
use heck::SnakeCase;
use quote::quote;
use syn::parse_macro_input;
#[proc_macro_attribute]
pub fn interface(
_args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let item_trait = parse_macro_input!(input as syn::ItemTrait);
let trait_name = item_trait.ident.to_string();
let mod_name: proc_macro2::TokenStream = item_trait
.ident
.to_string()
.to_snake_case()
.parse()
.unwrap();
let methods: Vec<proc_macro2::TokenStream> = item_trait
.items
.iter()
.filter_map(|trait_item: &syn::TraitItem| match trait_item {
syn::TraitItem::Method(m) => Some(m),
_ => None,
})
.map(|method: &syn::TraitItemMethod| {
let method_name = &method.sig.ident;
let args: Vec<&syn::PatType> = method
.sig
.inputs
.iter()
.filter_map(|arg: &syn::FnArg| match arg {
syn::FnArg::Typed(pat_ty) => Some(pat_ty),
_ => panic!("Invalid syntax. No self allowed."),
})
.filter(|pat_ty| {
let mut ty = parser::tts_to_string(&pat_ty.ty);
ty.retain(|s| !s.is_whitespace());
!ty.starts_with("Context<")
})
.collect();
let args_no_tys: Vec<&Box<syn::Pat>> = args
.iter()
.map(|arg| {
&arg.pat
})
.collect();
let args_struct = {
if args.is_empty() {
quote! {
use anchor_lang::prelude::borsh;
#[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
struct Args;
}
} else {
quote! {
use anchor_lang::prelude::borsh;
#[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
struct Args {
#(#args),*
}
}
}
};
let sighash_arr = anchor_syn::codegen::program::common::sighash(&trait_name, &method_name.to_string());
let sighash_tts: proc_macro2::TokenStream =
format!("{:?}", sighash_arr).parse().unwrap();
quote! {
pub fn #method_name<'a,'b, 'c, 'info, T: anchor_lang::Accounts<'info> + anchor_lang::ToAccountMetas + anchor_lang::ToAccountInfos<'info>>(
ctx: anchor_lang::context::CpiContext<'a, 'b, 'c, 'info, T>,
#(#args),*
) -> anchor_lang::Result<()> {
#args_struct
let ix = {
let ix = Args {
#(#args_no_tys),*
};
let mut ix_data = anchor_lang::AnchorSerialize::try_to_vec(&ix)
.map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotSerialize)?;
let mut data = #sighash_tts.to_vec();
data.append(&mut ix_data);
let accounts = ctx.to_account_metas(None);
anchor_lang::solana_program::instruction::Instruction {
program_id: *ctx.program.key,
accounts,
data,
}
};
let mut acc_infos = ctx.to_account_infos();
acc_infos.push(ctx.program.clone());
anchor_lang::solana_program::program::invoke_signed(
&ix,
&acc_infos,
ctx.signer_seeds,
).map_err(Into::into)
}
}
})
.collect();
proc_macro::TokenStream::from(quote! {
#item_trait
mod #mod_name {
use super::*;
#(#methods)*
}
})
}