lang/syn: Allow state structs with no ctor or impl block
This commit is contained in:
parent
845df6d196
commit
a780002683
|
@ -17,6 +17,7 @@ incremented for features.
|
|||
* cli: Stream program logs to `.anchor/program-logs` directory when testing.
|
||||
* spl: Add shared memory api.
|
||||
* lang/attribute/access-control: Allow specifying multiple modifier functions.
|
||||
* lang/syn: Allow state structs that don't have a ctor or impl block (just trait implementations).
|
||||
|
||||
## [0.2.0] - 2021-02-08
|
||||
|
||||
|
|
|
@ -13,15 +13,7 @@ pub mod counter_auth {
|
|||
use super::*;
|
||||
|
||||
#[state]
|
||||
pub struct CounterAuth {}
|
||||
|
||||
// TODO: remove this impl block after addressing
|
||||
// https://github.com/project-serum/anchor/issues/71.
|
||||
impl CounterAuth {
|
||||
pub fn new(_ctx: Context<Empty>) -> Result<Self, ProgramError> {
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
||||
pub struct CounterAuth;
|
||||
|
||||
impl<'info> Auth<'info, Empty> for CounterAuth {
|
||||
fn is_authorized(_ctx: Context<Empty>, current: u64, new: u64) -> ProgramResult {
|
||||
|
|
|
@ -69,39 +69,14 @@ pub fn generate_dispatch(program: &Program) -> proc_macro2::TokenStream {
|
|||
// Dispatch the state constructor.
|
||||
let ctor_state_dispatch_arm = match &program.state {
|
||||
None => quote! { /* no-op */ },
|
||||
Some(state) => {
|
||||
let variant_arm = generate_ctor_variant(state);
|
||||
let ctor_args = generate_ctor_args(state);
|
||||
let ix_name: proc_macro2::TokenStream = generate_ctor_variant_name().parse().unwrap();
|
||||
let sighash_arr = sighash_ctor();
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
quote! {
|
||||
#sighash_tts => {
|
||||
let ix = instruction::#ix_name::deserialize(&mut instruction_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
let instruction::#variant_arm = ix;
|
||||
__private::__ctor(program_id, accounts, #(#ctor_args),*)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Dispatch the state impl instructions.
|
||||
let state_dispatch_arms: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => vec![],
|
||||
Some(s) => s
|
||||
.methods
|
||||
.iter()
|
||||
.map(|rpc: &crate::StateRpc| {
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
rpc.args.iter().map(|arg| &arg.name).collect();
|
||||
let name = &rpc.raw_method.sig.ident.to_string();
|
||||
let rpc_name: proc_macro2::TokenStream = { format!("__{}", name).parse().unwrap() };
|
||||
let variant_arm =
|
||||
generate_ix_variant(rpc.raw_method.sig.ident.to_string(), &rpc.args, true);
|
||||
let ix_name = generate_ix_variant_name(rpc.raw_method.sig.ident.to_string(), true);
|
||||
let sighash_arr = sighash(SIGHASH_STATE_NAMESPACE, &name);
|
||||
Some(state) => match state.ctor_and_anchor.is_some() {
|
||||
false => quote! {},
|
||||
true => {
|
||||
let variant_arm = generate_ctor_variant(state);
|
||||
let ctor_args = generate_ctor_args(state);
|
||||
let ix_name: proc_macro2::TokenStream =
|
||||
generate_ctor_variant_name().parse().unwrap();
|
||||
let sighash_arr = sighash_ctor();
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
quote! {
|
||||
|
@ -109,11 +84,50 @@ pub fn generate_dispatch(program: &Program) -> proc_macro2::TokenStream {
|
|||
let ix = instruction::#ix_name::deserialize(&mut instruction_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
let instruction::#variant_arm = ix;
|
||||
__private::#rpc_name(program_id, accounts, #(#rpc_arg_names),*)
|
||||
__private::__ctor(program_id, accounts, #(#ctor_args),*)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Dispatch the state impl instructions.
|
||||
let state_dispatch_arms: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => vec![],
|
||||
Some(s) => s
|
||||
.impl_block_and_methods
|
||||
.as_ref()
|
||||
.map(|(_impl_block, methods)| {
|
||||
methods
|
||||
.iter()
|
||||
.map(|rpc: &crate::StateRpc| {
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
rpc.args.iter().map(|arg| &arg.name).collect();
|
||||
let name = &rpc.raw_method.sig.ident.to_string();
|
||||
let rpc_name: proc_macro2::TokenStream =
|
||||
{ format!("__{}", name).parse().unwrap() };
|
||||
let variant_arm = generate_ix_variant(
|
||||
rpc.raw_method.sig.ident.to_string(),
|
||||
&rpc.args,
|
||||
true,
|
||||
);
|
||||
let ix_name =
|
||||
generate_ix_variant_name(rpc.raw_method.sig.ident.to_string(), true);
|
||||
let sighash_arr = sighash(SIGHASH_STATE_NAMESPACE, &name);
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
quote! {
|
||||
#sighash_tts => {
|
||||
let ix = instruction::#ix_name::deserialize(&mut instruction_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
let instruction::#variant_arm = ix;
|
||||
__private::#rpc_name(program_id, accounts, #(#rpc_arg_names),*)
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect(),
|
||||
.unwrap_or(vec![]),
|
||||
};
|
||||
|
||||
// Dispatch all trait interface implementations.
|
||||
|
@ -121,54 +135,59 @@ pub fn generate_dispatch(program: &Program) -> proc_macro2::TokenStream {
|
|||
None => vec![],
|
||||
Some(s) => s
|
||||
.interfaces
|
||||
.iter()
|
||||
.flat_map(|iface: &crate::StateInterface| {
|
||||
iface
|
||||
.methods
|
||||
.as_ref()
|
||||
.map(|interfaces| {
|
||||
interfaces
|
||||
.iter()
|
||||
.map(|m: &crate::StateRpc| {
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
m.args.iter().map(|arg| &arg.name).collect();
|
||||
let name = &m.raw_method.sig.ident.to_string();
|
||||
let rpc_name: proc_macro2::TokenStream = format!("__{}_{}", iface.trait_name, name).parse().unwrap();
|
||||
let raw_args: Vec<&syn::PatType> = m
|
||||
.args
|
||||
.flat_map(|iface: &crate::StateInterface| {
|
||||
iface
|
||||
.methods
|
||||
.iter()
|
||||
.map(|arg: &crate::RpcArg| &arg.raw_arg)
|
||||
.collect();
|
||||
let sighash_arr = sighash(&iface.trait_name, &m.ident.to_string());
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
let args_struct = {
|
||||
if m.args.len() == 0 {
|
||||
.map(|m: &crate::StateRpc| {
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
m.args.iter().map(|arg| &arg.name).collect();
|
||||
let name = &m.raw_method.sig.ident.to_string();
|
||||
let rpc_name: proc_macro2::TokenStream = format!("__{}_{}", iface.trait_name, name).parse().unwrap();
|
||||
let raw_args: Vec<&syn::PatType> = m
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg: &crate::RpcArg| &arg.raw_arg)
|
||||
.collect();
|
||||
let sighash_arr = sighash(&iface.trait_name, &m.ident.to_string());
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
let args_struct = {
|
||||
if m.args.len() == 0 {
|
||||
quote! {
|
||||
#[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
|
||||
struct Args;
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
|
||||
struct Args {
|
||||
#(#raw_args),*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
#[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
|
||||
struct Args;
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
|
||||
struct Args {
|
||||
#(#raw_args),*
|
||||
#sighash_tts => {
|
||||
#args_struct
|
||||
let ix = Args::deserialize(&mut instruction_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
let Args {
|
||||
#(#rpc_arg_names),*
|
||||
} = ix;
|
||||
__private::#rpc_name(program_id, accounts, #(#rpc_arg_names),*)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
#sighash_tts => {
|
||||
#args_struct
|
||||
let ix = Args::deserialize(&mut instruction_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
let Args {
|
||||
#(#rpc_arg_names),*
|
||||
} = ix;
|
||||
__private::#rpc_name(program_id, accounts, #(#rpc_arg_names),*)
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<proc_macro2::TokenStream>>()
|
||||
})
|
||||
.collect::<Vec<proc_macro2::TokenStream>>()
|
||||
.collect()
|
||||
})
|
||||
.collect(),
|
||||
.unwrap_or(vec![])
|
||||
};
|
||||
|
||||
// Dispatch all global instructions.
|
||||
|
@ -346,234 +365,246 @@ pub fn generate_non_inlined_handlers(program: &Program) -> proc_macro2::TokenStr
|
|||
};
|
||||
let non_inlined_ctor: proc_macro2::TokenStream = match &program.state {
|
||||
None => quote! {},
|
||||
Some(state) => {
|
||||
let ctor_typed_args = generate_ctor_typed_args(state);
|
||||
let ctor_untyped_args = generate_ctor_args(state);
|
||||
let name = &state.strct.ident;
|
||||
let mod_name = &program.name;
|
||||
let anchor_ident = &state.ctor_anchor;
|
||||
quote! {
|
||||
// One time state account initializer. Will faill on subsequent
|
||||
// invocations.
|
||||
#[inline(never)]
|
||||
pub fn __ctor(program_id: &Pubkey, accounts: &[AccountInfo], #(#ctor_typed_args),*) -> ProgramResult {
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
|
||||
// Deserialize accounts.
|
||||
let ctor_accounts = anchor_lang::Ctor::try_accounts(program_id, &mut remaining_accounts)?;
|
||||
let mut ctor_user_def_accounts = #anchor_ident::try_accounts(program_id, &mut remaining_accounts)?;
|
||||
|
||||
// Invoke the ctor.
|
||||
let instance = #mod_name::#name::new(
|
||||
anchor_lang::Context::new(
|
||||
program_id,
|
||||
&mut ctor_user_def_accounts,
|
||||
remaining_accounts,
|
||||
),
|
||||
#(#ctor_untyped_args),*
|
||||
)?;
|
||||
|
||||
// Create the solana account for the ctor data.
|
||||
let from = ctor_accounts.from.key;
|
||||
let (base, nonce) = Pubkey::find_program_address(&[], ctor_accounts.program.key);
|
||||
let seed = anchor_lang::ProgramState::<#name>::seed();
|
||||
let owner = ctor_accounts.program.key;
|
||||
let to = Pubkey::create_with_seed(&base, seed, owner).unwrap();
|
||||
// Add 8 for the account discriminator.
|
||||
let space = 8 + instance.try_to_vec().map_err(|_| ProgramError::Custom(1))?.len();
|
||||
let lamports = ctor_accounts.rent.minimum_balance(space);
|
||||
let seeds = &[&[nonce][..]];
|
||||
let ix = anchor_lang::solana_program::system_instruction::create_account_with_seed(
|
||||
from,
|
||||
&to,
|
||||
&base,
|
||||
seed,
|
||||
lamports,
|
||||
space as u64,
|
||||
owner,
|
||||
);
|
||||
anchor_lang::solana_program::program::invoke_signed(
|
||||
&ix,
|
||||
&[
|
||||
ctor_accounts.from.clone(),
|
||||
ctor_accounts.to.clone(),
|
||||
ctor_accounts.base.clone(),
|
||||
ctor_accounts.system_program.clone(),
|
||||
],
|
||||
&[seeds],
|
||||
)?;
|
||||
|
||||
// Serialize the state and save it to storage.
|
||||
ctor_user_def_accounts.exit(program_id)?;
|
||||
let mut data = ctor_accounts.to.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut cursor = std::io::Cursor::new(dst);
|
||||
instance.try_serialize(&mut cursor)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let non_inlined_state_handlers: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => vec![],
|
||||
Some(state) => state
|
||||
.methods
|
||||
.iter()
|
||||
.map(|rpc| {
|
||||
let rpc_params: Vec<_> = rpc.args.iter().map(|arg| &arg.raw_arg).collect();
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
rpc.args.iter().map(|arg| &arg.name).collect();
|
||||
let private_rpc_name: proc_macro2::TokenStream = {
|
||||
let n = format!("__{}", &rpc.raw_method.sig.ident.to_string());
|
||||
n.parse().unwrap()
|
||||
};
|
||||
let rpc_name = &rpc.raw_method.sig.ident;
|
||||
let state_ty: proc_macro2::TokenStream = state.name.parse().unwrap();
|
||||
let anchor_ident = &rpc.anchor_ident;
|
||||
Some(state) => match state.ctor_and_anchor.as_ref() {
|
||||
None => quote! {},
|
||||
Some((_ctor, anchor_ident)) => {
|
||||
let ctor_typed_args = generate_ctor_typed_args(state);
|
||||
let ctor_untyped_args = generate_ctor_args(state);
|
||||
let name = &state.strct.ident;
|
||||
let mod_name = &program.name;
|
||||
quote! {
|
||||
// One time state account initializer. Will faill on subsequent
|
||||
// invocations.
|
||||
#[inline(never)]
|
||||
pub fn #private_rpc_name(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
#(#rpc_params),*
|
||||
) -> ProgramResult {
|
||||
|
||||
pub fn __ctor(program_id: &Pubkey, accounts: &[AccountInfo], #(#ctor_typed_args),*) -> ProgramResult {
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
if remaining_accounts.len() == 0 {
|
||||
return Err(ProgramError::Custom(1)); // todo
|
||||
}
|
||||
|
||||
// Deserialize the program state account.
|
||||
let state_account = &remaining_accounts[0];
|
||||
let mut state: #state_ty = {
|
||||
let data = state_account.try_borrow_data()?;
|
||||
let mut sliced: &[u8] = &data;
|
||||
anchor_lang::AccountDeserialize::try_deserialize(&mut sliced)?
|
||||
};
|
||||
// Deserialize accounts.
|
||||
let ctor_accounts = anchor_lang::Ctor::try_accounts(program_id, &mut remaining_accounts)?;
|
||||
let mut ctor_user_def_accounts = #anchor_ident::try_accounts(program_id, &mut remaining_accounts)?;
|
||||
|
||||
remaining_accounts = &remaining_accounts[1..];
|
||||
|
||||
// Deserialize the program's execution context.
|
||||
let mut accounts = #anchor_ident::try_accounts(
|
||||
program_id,
|
||||
&mut remaining_accounts,
|
||||
// Invoke the ctor.
|
||||
let instance = #mod_name::#name::new(
|
||||
anchor_lang::Context::new(
|
||||
program_id,
|
||||
&mut ctor_user_def_accounts,
|
||||
remaining_accounts,
|
||||
),
|
||||
#(#ctor_untyped_args),*
|
||||
)?;
|
||||
let ctx = Context::new(program_id, &mut accounts, remaining_accounts);
|
||||
|
||||
// Execute user defined function.
|
||||
state.#rpc_name(
|
||||
ctx,
|
||||
#(#rpc_arg_names),*
|
||||
// Create the solana account for the ctor data.
|
||||
let from = ctor_accounts.from.key;
|
||||
let (base, nonce) = Pubkey::find_program_address(&[], ctor_accounts.program.key);
|
||||
let seed = anchor_lang::ProgramState::<#name>::seed();
|
||||
let owner = ctor_accounts.program.key;
|
||||
let to = Pubkey::create_with_seed(&base, seed, owner).unwrap();
|
||||
// Add 8 for the account discriminator.
|
||||
let space = 8 + instance.try_to_vec().map_err(|_| ProgramError::Custom(1))?.len();
|
||||
let lamports = ctor_accounts.rent.minimum_balance(space);
|
||||
let seeds = &[&[nonce][..]];
|
||||
let ix = anchor_lang::solana_program::system_instruction::create_account_with_seed(
|
||||
from,
|
||||
&to,
|
||||
&base,
|
||||
seed,
|
||||
lamports,
|
||||
space as u64,
|
||||
owner,
|
||||
);
|
||||
anchor_lang::solana_program::program::invoke_signed(
|
||||
&ix,
|
||||
&[
|
||||
ctor_accounts.from.clone(),
|
||||
ctor_accounts.to.clone(),
|
||||
ctor_accounts.base.clone(),
|
||||
ctor_accounts.system_program.clone(),
|
||||
],
|
||||
&[seeds],
|
||||
)?;
|
||||
|
||||
// Serialize the state and save it to storage.
|
||||
accounts.exit(program_id)?;
|
||||
let mut data = state_account.try_borrow_mut_data()?;
|
||||
ctor_user_def_accounts.exit(program_id)?;
|
||||
let mut data = ctor_accounts.to.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut cursor = std::io::Cursor::new(dst);
|
||||
state.try_serialize(&mut cursor)?;
|
||||
instance.try_serialize(&mut cursor)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
},
|
||||
};
|
||||
let non_inlined_state_trait_handlers: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => Vec::new(),
|
||||
let non_inlined_state_handlers: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => vec![],
|
||||
Some(state) => state
|
||||
.interfaces
|
||||
.iter()
|
||||
.flat_map(|iface: &crate::StateInterface| {
|
||||
iface
|
||||
.methods
|
||||
.impl_block_and_methods
|
||||
.as_ref()
|
||||
.map(|(_impl_block, methods)| {
|
||||
methods
|
||||
.iter()
|
||||
.map(|rpc| {
|
||||
let rpc_params: Vec<_> = rpc.args.iter().map(|arg| &arg.raw_arg).collect();
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
rpc.args.iter().map(|arg| &arg.name).collect();
|
||||
let private_rpc_name: proc_macro2::TokenStream = {
|
||||
let n = format!("__{}_{}", iface.trait_name, &rpc.raw_method.sig.ident.to_string());
|
||||
let n = format!("__{}", &rpc.raw_method.sig.ident.to_string());
|
||||
n.parse().unwrap()
|
||||
};
|
||||
let rpc_name = &rpc.raw_method.sig.ident;
|
||||
let state_ty: proc_macro2::TokenStream = state.name.parse().unwrap();
|
||||
let anchor_ident = &rpc.anchor_ident;
|
||||
quote! {
|
||||
#[inline(never)]
|
||||
pub fn #private_rpc_name(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
#(#rpc_params),*
|
||||
) -> ProgramResult {
|
||||
|
||||
if rpc.has_receiver {
|
||||
quote! {
|
||||
#[inline(never)]
|
||||
pub fn #private_rpc_name(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
#(#rpc_params),*
|
||||
) -> ProgramResult {
|
||||
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
if remaining_accounts.len() == 0 {
|
||||
return Err(ProgramError::Custom(1)); // todo
|
||||
}
|
||||
|
||||
// Deserialize the program state account.
|
||||
let state_account = &remaining_accounts[0];
|
||||
let mut state: #state_ty = {
|
||||
let data = state_account.try_borrow_data()?;
|
||||
let mut sliced: &[u8] = &data;
|
||||
anchor_lang::AccountDeserialize::try_deserialize(&mut sliced)?
|
||||
};
|
||||
|
||||
remaining_accounts = &remaining_accounts[1..];
|
||||
|
||||
// Deserialize the program's execution context.
|
||||
let mut accounts = #anchor_ident::try_accounts(
|
||||
program_id,
|
||||
&mut remaining_accounts,
|
||||
)?;
|
||||
let ctx = Context::new(program_id, &mut accounts, remaining_accounts);
|
||||
|
||||
// Execute user defined function.
|
||||
state.#rpc_name(
|
||||
ctx,
|
||||
#(#rpc_arg_names),*
|
||||
)?;
|
||||
|
||||
// Serialize the state and save it to storage.
|
||||
accounts.exit(program_id)?;
|
||||
let mut data = state_account.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut cursor = std::io::Cursor::new(dst);
|
||||
state.try_serialize(&mut cursor)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let state_name: proc_macro2::TokenStream = state.name.parse().unwrap();
|
||||
quote! {
|
||||
#[inline(never)]
|
||||
pub fn #private_rpc_name(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
#(#rpc_params),*
|
||||
) -> ProgramResult {
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
let mut accounts = #anchor_ident::try_accounts(
|
||||
program_id,
|
||||
&mut remaining_accounts,
|
||||
)?;
|
||||
#state_name::#rpc_name(
|
||||
Context::new(program_id, &mut accounts, remaining_accounts),
|
||||
#(#rpc_arg_names),*
|
||||
)?;
|
||||
accounts.exit(program_id)
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
if remaining_accounts.len() == 0 {
|
||||
return Err(ProgramError::Custom(1)); // todo
|
||||
}
|
||||
|
||||
// Deserialize the program state account.
|
||||
let state_account = &remaining_accounts[0];
|
||||
let mut state: #state_ty = {
|
||||
let data = state_account.try_borrow_data()?;
|
||||
let mut sliced: &[u8] = &data;
|
||||
anchor_lang::AccountDeserialize::try_deserialize(&mut sliced)?
|
||||
};
|
||||
|
||||
remaining_accounts = &remaining_accounts[1..];
|
||||
|
||||
// Deserialize the program's execution context.
|
||||
let mut accounts = #anchor_ident::try_accounts(
|
||||
program_id,
|
||||
&mut remaining_accounts,
|
||||
)?;
|
||||
let ctx = Context::new(program_id, &mut accounts, remaining_accounts);
|
||||
|
||||
// Execute user defined function.
|
||||
state.#rpc_name(
|
||||
ctx,
|
||||
#(#rpc_arg_names),*
|
||||
)?;
|
||||
|
||||
// Serialize the state and save it to storage.
|
||||
accounts.exit(program_id)?;
|
||||
let mut data = state_account.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut cursor = std::io::Cursor::new(dst);
|
||||
state.try_serialize(&mut cursor)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<proc_macro2::TokenStream>>()
|
||||
.collect()
|
||||
})
|
||||
.collect(),
|
||||
.unwrap_or(vec![]),
|
||||
};
|
||||
let non_inlined_state_trait_handlers: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => Vec::new(),
|
||||
Some(state) => state
|
||||
.interfaces
|
||||
.as_ref()
|
||||
.map(|interfaces| {
|
||||
interfaces
|
||||
.iter()
|
||||
.flat_map(|iface: &crate::StateInterface| {
|
||||
iface
|
||||
.methods
|
||||
.iter()
|
||||
.map(|rpc| {
|
||||
let rpc_params: Vec<_> = rpc.args.iter().map(|arg| &arg.raw_arg).collect();
|
||||
let rpc_arg_names: Vec<&syn::Ident> =
|
||||
rpc.args.iter().map(|arg| &arg.name).collect();
|
||||
let private_rpc_name: proc_macro2::TokenStream = {
|
||||
let n = format!("__{}_{}", iface.trait_name, &rpc.raw_method.sig.ident.to_string());
|
||||
n.parse().unwrap()
|
||||
};
|
||||
let rpc_name = &rpc.raw_method.sig.ident;
|
||||
let state_ty: proc_macro2::TokenStream = state.name.parse().unwrap();
|
||||
let anchor_ident = &rpc.anchor_ident;
|
||||
|
||||
if rpc.has_receiver {
|
||||
quote! {
|
||||
#[inline(never)]
|
||||
pub fn #private_rpc_name(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
#(#rpc_params),*
|
||||
) -> ProgramResult {
|
||||
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
if remaining_accounts.len() == 0 {
|
||||
return Err(ProgramError::Custom(1)); // todo
|
||||
}
|
||||
|
||||
// Deserialize the program state account.
|
||||
let state_account = &remaining_accounts[0];
|
||||
let mut state: #state_ty = {
|
||||
let data = state_account.try_borrow_data()?;
|
||||
let mut sliced: &[u8] = &data;
|
||||
anchor_lang::AccountDeserialize::try_deserialize(&mut sliced)?
|
||||
};
|
||||
|
||||
remaining_accounts = &remaining_accounts[1..];
|
||||
|
||||
// Deserialize the program's execution context.
|
||||
let mut accounts = #anchor_ident::try_accounts(
|
||||
program_id,
|
||||
&mut remaining_accounts,
|
||||
)?;
|
||||
let ctx = Context::new(program_id, &mut accounts, remaining_accounts);
|
||||
|
||||
// Execute user defined function.
|
||||
state.#rpc_name(
|
||||
ctx,
|
||||
#(#rpc_arg_names),*
|
||||
)?;
|
||||
|
||||
// Serialize the state and save it to storage.
|
||||
accounts.exit(program_id)?;
|
||||
let mut data = state_account.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut cursor = std::io::Cursor::new(dst);
|
||||
state.try_serialize(&mut cursor)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let state_name: proc_macro2::TokenStream = state.name.parse().unwrap();
|
||||
quote! {
|
||||
#[inline(never)]
|
||||
pub fn #private_rpc_name(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
#(#rpc_params),*
|
||||
) -> ProgramResult {
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
let mut accounts = #anchor_ident::try_accounts(
|
||||
program_id,
|
||||
&mut remaining_accounts,
|
||||
)?;
|
||||
#state_name::#rpc_name(
|
||||
Context::new(program_id, &mut accounts, remaining_accounts),
|
||||
#(#rpc_arg_names),*
|
||||
)?;
|
||||
accounts.exit(program_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<proc_macro2::TokenStream>>()
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or(Vec::new()),
|
||||
};
|
||||
let non_inlined_handlers: Vec<proc_macro2::TokenStream> = program
|
||||
.rpcs
|
||||
|
@ -663,42 +694,50 @@ pub fn generate_ctor_typed_variant_with_semi(program: &Program) -> proc_macro2::
|
|||
|
||||
fn generate_ctor_typed_args(state: &State) -> Vec<syn::PatType> {
|
||||
state
|
||||
.ctor
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => {
|
||||
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
||||
arg_str.retain(|c| !c.is_whitespace());
|
||||
if arg_str.starts_with("Context<") {
|
||||
return None;
|
||||
}
|
||||
Some(pat_ty.clone())
|
||||
}
|
||||
_ => panic!("Invalid syntaxe,"),
|
||||
.ctor_and_anchor
|
||||
.as_ref()
|
||||
.map(|(ctor, _anchor_ident)| {
|
||||
ctor.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => {
|
||||
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
||||
arg_str.retain(|c| !c.is_whitespace());
|
||||
if arg_str.starts_with("Context<") {
|
||||
return None;
|
||||
}
|
||||
Some(pat_ty.clone())
|
||||
}
|
||||
_ => panic!("Invalid syntaxe,"),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
.unwrap_or(Vec::new())
|
||||
}
|
||||
|
||||
fn generate_ctor_args(state: &State) -> Vec<Box<syn::Pat>> {
|
||||
state
|
||||
.ctor
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => {
|
||||
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
||||
arg_str.retain(|c| !c.is_whitespace());
|
||||
if arg_str.starts_with("Context<") {
|
||||
return None;
|
||||
}
|
||||
Some(pat_ty.pat.clone())
|
||||
}
|
||||
_ => panic!(""),
|
||||
.ctor_and_anchor
|
||||
.as_ref()
|
||||
.map(|(ctor, _anchor_ident)| {
|
||||
ctor.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => {
|
||||
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
||||
arg_str.retain(|c| !c.is_whitespace());
|
||||
if arg_str.starts_with("Context<") {
|
||||
return None;
|
||||
}
|
||||
Some(pat_ty.pat.clone())
|
||||
}
|
||||
_ => panic!(""),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
.unwrap_or(Vec::new())
|
||||
}
|
||||
|
||||
pub fn generate_ix_variant(
|
||||
|
@ -750,62 +789,67 @@ pub fn generate_instructions(program: &Program) -> proc_macro2::TokenStream {
|
|||
let state_method_variants: Vec<proc_macro2::TokenStream> = match &program.state {
|
||||
None => vec![],
|
||||
Some(state) => state
|
||||
.methods
|
||||
.iter()
|
||||
.map(|method| {
|
||||
let rpc_name_camel: proc_macro2::TokenStream = {
|
||||
let name = format!(
|
||||
"__{}",
|
||||
&method.raw_method.sig.ident.to_string().to_camel_case(),
|
||||
);
|
||||
name.parse().unwrap()
|
||||
};
|
||||
let raw_args: Vec<proc_macro2::TokenStream> = method
|
||||
.args
|
||||
.impl_block_and_methods
|
||||
.as_ref()
|
||||
.map(|(_impl_block, methods)| {
|
||||
methods
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
format!("pub {}", parser::tts_to_string(&arg.raw_arg))
|
||||
.parse()
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
.map(|method| {
|
||||
let rpc_name_camel: proc_macro2::TokenStream = {
|
||||
let name = format!(
|
||||
"__{}",
|
||||
&method.raw_method.sig.ident.to_string().to_camel_case(),
|
||||
);
|
||||
name.parse().unwrap()
|
||||
};
|
||||
let raw_args: Vec<proc_macro2::TokenStream> = method
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
format!("pub {}", parser::tts_to_string(&arg.raw_arg))
|
||||
.parse()
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let ix_data_trait = {
|
||||
let name = method.raw_method.sig.ident.to_string();
|
||||
let sighash_arr = sighash(SIGHASH_GLOBAL_NAMESPACE, &name);
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
quote! {
|
||||
impl anchor_lang::InstructionData for #rpc_name_camel {
|
||||
fn data(&self) -> Vec<u8> {
|
||||
let mut d = #sighash_tts.to_vec();
|
||||
d.append(&mut self.try_to_vec().expect("Should always serialize"));
|
||||
d
|
||||
let ix_data_trait = {
|
||||
let name = method.raw_method.sig.ident.to_string();
|
||||
let sighash_arr = sighash(SIGHASH_GLOBAL_NAMESPACE, &name);
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
quote! {
|
||||
impl anchor_lang::InstructionData for #rpc_name_camel {
|
||||
fn data(&self) -> Vec<u8> {
|
||||
let mut d = #sighash_tts.to_vec();
|
||||
d.append(&mut self.try_to_vec().expect("Should always serialize"));
|
||||
d
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If no args, output a "unit" variant instead of a struct variant.
|
||||
if method.args.len() == 0 {
|
||||
quote! {
|
||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||
pub struct #rpc_name_camel;
|
||||
|
||||
#ix_data_trait
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||
pub struct #rpc_name_camel {
|
||||
#(#raw_args),*
|
||||
}
|
||||
|
||||
#ix_data_trait
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If no args, output a "unit" variant instead of a struct variant.
|
||||
if method.args.len() == 0 {
|
||||
quote! {
|
||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||
pub struct #rpc_name_camel;
|
||||
|
||||
#ix_data_trait
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||
pub struct #rpc_name_camel {
|
||||
#(#raw_args),*
|
||||
}
|
||||
|
||||
#ix_data_trait
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect(),
|
||||
.unwrap_or(Vec::new()),
|
||||
};
|
||||
let variants: Vec<proc_macro2::TokenStream> = program
|
||||
.rpcs
|
||||
|
@ -876,16 +920,18 @@ pub fn generate_instructions(program: &Program) -> proc_macro2::TokenStream {
|
|||
fn generate_accounts(program: &Program) -> proc_macro2::TokenStream {
|
||||
let mut accounts = std::collections::HashSet::new();
|
||||
|
||||
// Got through state accounts.
|
||||
// Go through state accounts.
|
||||
if let Some(state) = &program.state {
|
||||
for rpc in &state.methods {
|
||||
let anchor_ident = &rpc.anchor_ident;
|
||||
// TODO: move to fn and share with accounts.rs.
|
||||
let macro_name = format!(
|
||||
"__client_accounts_{}",
|
||||
anchor_ident.to_string().to_snake_case()
|
||||
);
|
||||
accounts.insert(macro_name);
|
||||
if let Some((_impl_block, methods)) = &state.impl_block_and_methods {
|
||||
for rpc in methods {
|
||||
let anchor_ident = &rpc.anchor_ident;
|
||||
// TODO: move to fn and share with accounts.rs.
|
||||
let macro_name = format!(
|
||||
"__client_accounts_{}",
|
||||
anchor_ident.to_string().to_snake_case()
|
||||
);
|
||||
accounts.insert(macro_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,11 +30,9 @@ pub struct Program {
|
|||
pub struct State {
|
||||
pub name: String,
|
||||
pub strct: syn::ItemStruct,
|
||||
pub impl_block: syn::ItemImpl,
|
||||
pub methods: Vec<StateRpc>,
|
||||
pub interfaces: Vec<StateInterface>,
|
||||
pub ctor: syn::ImplItemMethod,
|
||||
pub ctor_anchor: syn::Ident, // TODO: consolidate this with ctor above.
|
||||
pub ctor_and_anchor: Option<(syn::ImplItemMethod, syn::Ident)>,
|
||||
pub impl_block_and_methods: Option<(syn::ItemImpl, Vec<StateRpc>)>,
|
||||
pub interfaces: Option<Vec<StateInterface>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -33,102 +33,112 @@ pub fn parse(filename: impl AsRef<Path>) -> Result<Idl> {
|
|||
acc_names
|
||||
};
|
||||
|
||||
let state = p.state.map(|state| {
|
||||
let mut methods = state
|
||||
.methods
|
||||
.iter()
|
||||
.map(|method: &StateRpc| {
|
||||
let name = method.ident.to_string().to_mixed_case();
|
||||
let args = method
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
let mut tts = proc_macro2::TokenStream::new();
|
||||
arg.raw_arg.ty.to_tokens(&mut tts);
|
||||
let ty = tts.to_string().parse().unwrap();
|
||||
IdlField {
|
||||
name: arg.name.to_string().to_mixed_case(),
|
||||
ty,
|
||||
}
|
||||
let state = match p.state {
|
||||
None => None,
|
||||
Some(state) => match state.ctor_and_anchor {
|
||||
None => None, // State struct defined but no implementation
|
||||
Some((ctor, anchor_ident)) => {
|
||||
let mut methods = state
|
||||
.impl_block_and_methods
|
||||
.map(|(_impl_block, methods)| {
|
||||
methods
|
||||
.iter()
|
||||
.map(|method: &StateRpc| {
|
||||
let name = method.ident.to_string().to_mixed_case();
|
||||
let args = method
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
let mut tts = proc_macro2::TokenStream::new();
|
||||
arg.raw_arg.ty.to_tokens(&mut tts);
|
||||
let ty = tts.to_string().parse().unwrap();
|
||||
IdlField {
|
||||
name: arg.name.to_string().to_mixed_case(),
|
||||
ty,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let accounts_strct =
|
||||
accs.get(&method.anchor_ident.to_string()).unwrap();
|
||||
let accounts = accounts_strct.idl_accounts(&accs);
|
||||
IdlStateMethod {
|
||||
name,
|
||||
args,
|
||||
accounts,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let accounts_strct = accs.get(&method.anchor_ident.to_string()).unwrap();
|
||||
let accounts = accounts_strct.idl_accounts(&accs);
|
||||
IdlStateMethod {
|
||||
name,
|
||||
args,
|
||||
accounts,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let ctor = {
|
||||
let name = "new".to_string();
|
||||
let args = state
|
||||
.ctor
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => {
|
||||
// TODO: this filtering should be donein the parser.
|
||||
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
||||
arg_str.retain(|c| !c.is_whitespace());
|
||||
if arg_str.starts_with("Context<") {
|
||||
return None;
|
||||
}
|
||||
Some(arg)
|
||||
.unwrap_or(Vec::new());
|
||||
let ctor = {
|
||||
let name = "new".to_string();
|
||||
let args = ctor
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => {
|
||||
// TODO: this filtering should be donein the parser.
|
||||
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
||||
arg_str.retain(|c| !c.is_whitespace());
|
||||
if arg_str.starts_with("Context<") {
|
||||
return None;
|
||||
}
|
||||
Some(arg)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(arg_typed) => {
|
||||
let mut tts = proc_macro2::TokenStream::new();
|
||||
arg_typed.ty.to_tokens(&mut tts);
|
||||
let ty = tts.to_string().parse().unwrap();
|
||||
IdlField {
|
||||
name: parser::tts_to_string(&arg_typed.pat).to_mixed_case(),
|
||||
ty,
|
||||
}
|
||||
}
|
||||
_ => panic!("Invalid syntax"),
|
||||
})
|
||||
.collect();
|
||||
let accounts_strct = accs.get(&anchor_ident.to_string()).unwrap();
|
||||
let accounts = accounts_strct.idl_accounts(&accs);
|
||||
IdlStateMethod {
|
||||
name,
|
||||
args,
|
||||
accounts,
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(arg_typed) => {
|
||||
let mut tts = proc_macro2::TokenStream::new();
|
||||
arg_typed.ty.to_tokens(&mut tts);
|
||||
let ty = tts.to_string().parse().unwrap();
|
||||
IdlField {
|
||||
name: parser::tts_to_string(&arg_typed.pat).to_mixed_case(),
|
||||
ty,
|
||||
}
|
||||
};
|
||||
|
||||
methods.insert(0, ctor);
|
||||
|
||||
let strct = {
|
||||
let fields = match state.strct.fields {
|
||||
syn::Fields::Named(f_named) => f_named
|
||||
.named
|
||||
.iter()
|
||||
.map(|f: &syn::Field| {
|
||||
let mut tts = proc_macro2::TokenStream::new();
|
||||
f.ty.to_tokens(&mut tts);
|
||||
let ty = tts.to_string().parse().unwrap();
|
||||
IdlField {
|
||||
name: f.ident.as_ref().unwrap().to_string().to_mixed_case(),
|
||||
ty,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<IdlField>>(),
|
||||
_ => panic!("State must be a struct"),
|
||||
};
|
||||
IdlTypeDef {
|
||||
name: state.name,
|
||||
ty: IdlTypeDefTy::Struct { fields },
|
||||
}
|
||||
_ => panic!("Invalid syntax"),
|
||||
})
|
||||
.collect();
|
||||
let accounts_strct = accs.get(&state.ctor_anchor.to_string()).unwrap();
|
||||
let accounts = accounts_strct.idl_accounts(&accs);
|
||||
IdlStateMethod {
|
||||
name,
|
||||
args,
|
||||
accounts,
|
||||
};
|
||||
|
||||
Some(IdlState { strct, methods })
|
||||
}
|
||||
};
|
||||
|
||||
methods.insert(0, ctor);
|
||||
|
||||
let strct = {
|
||||
let fields = match state.strct.fields {
|
||||
syn::Fields::Named(f_named) => f_named
|
||||
.named
|
||||
.iter()
|
||||
.map(|f: &syn::Field| {
|
||||
let mut tts = proc_macro2::TokenStream::new();
|
||||
f.ty.to_tokens(&mut tts);
|
||||
let ty = tts.to_string().parse().unwrap();
|
||||
IdlField {
|
||||
name: f.ident.as_ref().unwrap().to_string().to_mixed_case(),
|
||||
ty,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<IdlField>>(),
|
||||
_ => panic!("State must be a struct"),
|
||||
};
|
||||
IdlTypeDef {
|
||||
name: state.name,
|
||||
ty: IdlTypeDefTy::Struct { fields },
|
||||
}
|
||||
};
|
||||
|
||||
IdlState { strct, methods }
|
||||
});
|
||||
},
|
||||
};
|
||||
let error = parse_error_enum(&f).map(|mut e| error::parse(&mut e));
|
||||
let error_codes = error.as_ref().map(|e| {
|
||||
e.codes
|
||||
|
|
|
@ -27,8 +27,9 @@ pub fn parse(program_mod: syn::ItemMod) -> Program {
|
|||
})
|
||||
.next();
|
||||
|
||||
let impl_block: Option<&syn::ItemImpl> = strct.map(|strct| {
|
||||
let item_impls = mod_content
|
||||
let impl_block: Option<syn::ItemImpl> = match strct {
|
||||
None => None,
|
||||
Some(strct) => mod_content
|
||||
.iter()
|
||||
.filter_map(|item| match item {
|
||||
syn::Item::Impl(item_impl) => {
|
||||
|
@ -40,13 +41,12 @@ pub fn parse(program_mod: syn::ItemMod) -> Program {
|
|||
if strct_name != impl_ty_str {
|
||||
return None;
|
||||
}
|
||||
Some(item_impl)
|
||||
Some(item_impl.clone())
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<&syn::ItemImpl>>();
|
||||
item_impls[0]
|
||||
});
|
||||
.next(),
|
||||
};
|
||||
|
||||
// All program interface implementations.
|
||||
let trait_impls: Option<Vec<StateInterface>> = strct.map(|_strct| {
|
||||
|
@ -84,83 +84,87 @@ pub fn parse(program_mod: syn::ItemMod) -> Program {
|
|||
let mut strct = strct.clone();
|
||||
strct.attrs = vec![];
|
||||
|
||||
let impl_block = impl_block.expect("Must exist if struct exists").clone();
|
||||
let (ctor, ctor_anchor) = impl_block
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|item: &syn::ImplItem| match item {
|
||||
syn::ImplItem::Method(m) => {
|
||||
if m.sig.ident.to_string() == "new" {
|
||||
let ctx_arg = m.sig.inputs.first().unwrap(); // todo: unwrap.
|
||||
match ctx_arg {
|
||||
syn::FnArg::Receiver(_) => panic!("invalid syntax"),
|
||||
syn::FnArg::Typed(arg) => {
|
||||
Some((m.clone(), extract_ident(&arg).clone()))
|
||||
let ctor_and_anchor = match &impl_block {
|
||||
None => None,
|
||||
Some(impl_block) => {
|
||||
impl_block
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|item: &syn::ImplItem| match item {
|
||||
syn::ImplItem::Method(m) => {
|
||||
if m.sig.ident.to_string() == "new" {
|
||||
let ctx_arg = m.sig.inputs.first().unwrap(); // todo: unwrap.
|
||||
match ctx_arg {
|
||||
syn::FnArg::Receiver(_) => panic!("invalid syntax"),
|
||||
syn::FnArg::Typed(arg) => {
|
||||
Some((m.clone(), extract_ident(&arg).clone()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.expect("Must exist if struct exists")
|
||||
.clone();
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.clone()
|
||||
}
|
||||
};
|
||||
|
||||
let methods: Vec<StateRpc> = impl_block
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|item: &syn::ImplItem| match item {
|
||||
syn::ImplItem::Method(m) => match m.sig.inputs.first() {
|
||||
None => None,
|
||||
Some(arg) => match arg {
|
||||
syn::FnArg::Typed(_) => None,
|
||||
syn::FnArg::Receiver(_) => {
|
||||
let mut args = m
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
syn::FnArg::Receiver(_) => None,
|
||||
syn::FnArg::Typed(arg) => Some(arg),
|
||||
})
|
||||
.map(|raw_arg| {
|
||||
let ident = match &*raw_arg.pat {
|
||||
syn::Pat::Ident(ident) => &ident.ident,
|
||||
_ => panic!("invalid syntax"),
|
||||
};
|
||||
RpcArg {
|
||||
name: ident.clone(),
|
||||
raw_arg: raw_arg.clone(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<RpcArg>>();
|
||||
// Remove the Anchor accounts argument
|
||||
let anchor = args.remove(0);
|
||||
let anchor_ident = extract_ident(&anchor.raw_arg).clone();
|
||||
let impl_block_and_methods = impl_block.map(|impl_block| {
|
||||
let methods: Vec<StateRpc> = impl_block
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|item: &syn::ImplItem| match item {
|
||||
syn::ImplItem::Method(m) => match m.sig.inputs.first() {
|
||||
None => None,
|
||||
Some(arg) => match arg {
|
||||
syn::FnArg::Typed(_) => None,
|
||||
syn::FnArg::Receiver(_) => {
|
||||
let mut args = m
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
syn::FnArg::Receiver(_) => None,
|
||||
syn::FnArg::Typed(arg) => Some(arg),
|
||||
})
|
||||
.map(|raw_arg| {
|
||||
let ident = match &*raw_arg.pat {
|
||||
syn::Pat::Ident(ident) => &ident.ident,
|
||||
_ => panic!("invalid syntax"),
|
||||
};
|
||||
RpcArg {
|
||||
name: ident.clone(),
|
||||
raw_arg: raw_arg.clone(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<RpcArg>>();
|
||||
// Remove the Anchor accounts argument
|
||||
let anchor = args.remove(0);
|
||||
let anchor_ident = extract_ident(&anchor.raw_arg).clone();
|
||||
|
||||
Some(StateRpc {
|
||||
raw_method: m.clone(),
|
||||
ident: m.sig.ident.clone(),
|
||||
args,
|
||||
anchor_ident,
|
||||
has_receiver: true,
|
||||
})
|
||||
}
|
||||
Some(StateRpc {
|
||||
raw_method: m.clone(),
|
||||
ident: m.sig.ident.clone(),
|
||||
args,
|
||||
anchor_ident,
|
||||
has_receiver: true,
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
(impl_block.clone(), methods)
|
||||
});
|
||||
State {
|
||||
name: strct.ident.to_string(),
|
||||
strct: strct.clone(),
|
||||
interfaces: trait_impls.expect("Some if state exists"),
|
||||
impl_block,
|
||||
ctor,
|
||||
ctor_anchor,
|
||||
methods,
|
||||
interfaces: trait_impls,
|
||||
impl_block_and_methods,
|
||||
ctor_and_anchor,
|
||||
}
|
||||
})
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue