nitrate_macro/lib.rs
1mod accounts;
2use accounts::generate_accounts;
3
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{parse_macro_input, DeriveInput};
7
8/// Annotates an enum with the `#[derive(Accounts)]` to derive instruction structs
9/// for each variant containing the accounts for the instruction.
10///
11/// This macro works in conjunction with the [Shank]'s `#[account]` attribute macro
12/// to define the accounts. It will create a struct for each variant of the enum that
13/// can be used to create a `Context` to easily access the accounts by their name.
14///
15/// Using a `Context` is more efficient than using the `next_account_info` iterator
16/// since the accounts structs encapsulate the order of accounts and they can be
17/// indexed directly.
18///
19/// [Shank]: https://github.com/metaplex-foundation/shank
20///
21/// # Examples
22///
23/// ```no_run
24/// #[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankInstruction, Accounts)]
25/// pub struct Instruction {
26/// /// Closes an uninitialized asset (buffer) account.
27/// ///
28/// /// You can only close the buffer account if it has not been used to create an asset.
29/// #[account(0, signer, writable, name="buffer", desc = "The unitialized buffer account")]
30/// #[account(1, writable, name="recipient", desc = "The account receiving refunded rent")]
31/// Close,
32///
33/// /// Burns an asset.
34/// #[account(0, writable, name="asset", desc = "Asset account")]
35/// #[account(1, signer, writable, name="signer", desc = "The owner or burn delegate of the asset")]
36/// #[account(2, optional, writable, name="recipient", desc = "The account receiving refunded rent")]
37/// #[account(3, optional, writable, name="group", desc = "Asset account of the group")]
38/// Burn,
39/// }
40/// ```
41///
42/// This will create a module `accounts` with a struct for each variant of the enum:
43/// ```no_run
44/// use nitrate::program::AccountInfo;
45///
46/// pub struct Close<'a> {
47/// pub buffer: &'a AccountInfo,
48/// pub recipient: &'a AccountInfo,
49/// }
50///
51/// pub struct Burn<'a> {
52/// pub asset: &'a AccountInfo,
53/// pub signer: &'a AccountInfo,
54/// pub recipient: Option<&'a AccountInfo>,
55/// pub group: Option<&'a AccountInfo>,
56/// }
57/// ```
58/// A `Context` can then be created to access the accounts of an instruction:
59/// ```no_run
60/// let ctx = Burn::context(accounts)?;
61/// msg!("Burn asset: {:?}", ctx.accounts.asset.key());
62/// ```
63#[proc_macro_derive(Accounts, attributes(account))]
64pub fn context_derive(input: TokenStream) -> TokenStream {
65 let ast = parse_macro_input!(input as DeriveInput);
66 let accounts = generate_accounts(ast);
67
68 match accounts {
69 Ok(accounts) => TokenStream::from(quote! {
70 pub mod accounts {
71 pub struct Context<T> {
72 pub accounts: T,
73 }
74
75 #accounts
76 }
77 }),
78 Err(error) => error.to_compile_error().into(),
79 }
80}