lib.rs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. extern crate proc_macro;
  2. use anchor_syn::parser;
  3. use heck::SnakeCase;
  4. use quote::quote;
  5. use syn::parse_macro_input;
  6. #[proc_macro_attribute]
  7. pub fn interface(
  8. _args: proc_macro::TokenStream,
  9. input: proc_macro::TokenStream,
  10. ) -> proc_macro::TokenStream {
  11. let item_trait = parse_macro_input!(input as syn::ItemTrait);
  12. let trait_name = item_trait.ident.to_string();
  13. let mod_name: proc_macro2::TokenStream = item_trait
  14. .ident
  15. .to_string()
  16. .to_snake_case()
  17. .parse()
  18. .unwrap();
  19. let methods: Vec<proc_macro2::TokenStream> = item_trait
  20. .items
  21. .iter()
  22. .filter_map(|trait_item: &syn::TraitItem| match trait_item {
  23. syn::TraitItem::Method(m) => Some(m),
  24. _ => None,
  25. })
  26. .map(|method: &syn::TraitItemMethod| {
  27. let method_name = &method.sig.ident;
  28. let args: Vec<&syn::PatType> = method
  29. .sig
  30. .inputs
  31. .iter()
  32. .filter_map(|arg: &syn::FnArg| match arg {
  33. syn::FnArg::Typed(pat_ty) => Some(pat_ty),
  34. // TODO: just map this to None once we allow this feature.
  35. _ => panic!("Invalid syntax. No self allowed."),
  36. })
  37. .filter_map(|pat_ty: &syn::PatType| {
  38. let mut ty = parser::tts_to_string(&pat_ty.ty);
  39. ty.retain(|s| !s.is_whitespace());
  40. if ty.starts_with("Context<") {
  41. None
  42. } else {
  43. Some(pat_ty)
  44. }
  45. })
  46. .collect();
  47. let args_no_tys: Vec<&Box<syn::Pat>> = args
  48. .iter()
  49. .map(|arg| {
  50. &arg.pat
  51. })
  52. .collect();
  53. let args_struct = {
  54. if args.len() == 0 {
  55. quote! {
  56. use anchor_lang::prelude::borsh;
  57. #[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
  58. struct Args;
  59. }
  60. } else {
  61. quote! {
  62. use anchor_lang::prelude::borsh;
  63. #[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
  64. struct Args {
  65. #(#args),*
  66. }
  67. }
  68. }
  69. };
  70. let sighash_arr = anchor_syn::codegen::program::sighash(&trait_name, &method_name.to_string());
  71. let sighash_tts: proc_macro2::TokenStream =
  72. format!("{:?}", sighash_arr).parse().unwrap();
  73. quote! {
  74. pub fn #method_name<'a,'b, 'c, 'info, T: anchor_lang::ToAccountMetas + anchor_lang::ToAccountInfos<'info>>(
  75. ctx: anchor_lang::CpiContext<'a, 'b, 'c, 'info, T>,
  76. #(#args),*
  77. ) -> anchor_lang::solana_program::entrypoint::ProgramResult {
  78. #args_struct
  79. let ix = {
  80. let ix = Args {
  81. #(#args_no_tys),*
  82. };
  83. let mut ix_data = anchor_lang::AnchorSerialize::try_to_vec(&ix)
  84. .map_err(|_| anchor_lang::solana_program::program_error::ProgramError::InvalidInstructionData)?;
  85. let mut data = #sighash_tts.to_vec();
  86. data.append(&mut ix_data);
  87. let accounts = ctx.accounts.to_account_metas(None);
  88. anchor_lang::solana_program::instruction::Instruction {
  89. program_id: *ctx.program.key,
  90. accounts,
  91. data,
  92. }
  93. };
  94. let mut acc_infos = ctx.accounts.to_account_infos();
  95. acc_infos.push(ctx.program.clone());
  96. anchor_lang::solana_program::program::invoke_signed(
  97. &ix,
  98. &acc_infos,
  99. ctx.signer_seeds,
  100. )
  101. }
  102. }
  103. })
  104. .collect();
  105. proc_macro::TokenStream::from(quote! {
  106. #item_trait
  107. mod #mod_name {
  108. use super::*;
  109. #(#methods)*
  110. }
  111. })
  112. }