use crate::*; use syn::ext::IdentExt; use syn::parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::Comma; use syn::{bracketed, Expr, Ident, LitStr, Token}; pub fn parse(f: &syn::Field, f_ty: Option<&Ty>) -> ParseResult { let mut constraints = ConstraintGroupBuilder::new(f_ty); for attr in f.attrs.iter().filter(is_account) { for c in attr.parse_args_with(Punctuated::::parse_terminated)? { constraints.add(c)?; } } let account_constraints = constraints.build()?; Ok(account_constraints) } pub fn is_account(attr: &&syn::Attribute) -> bool { attr.path .get_ident() .map_or(false, |ident| ident == "account") } // Parses a single constraint from a parse stream for `#[account()]`. pub fn parse_token(stream: ParseStream) -> ParseResult { let is_lit = stream.peek(LitStr); if is_lit { let lit: LitStr = stream.parse()?; let c = ConstraintToken::Literal(Context::new(lit.span(), ConstraintLiteral { lit })); return Ok(c); } let ident = stream.call(Ident::parse_any)?; let kw = ident.to_string(); let c = match kw.as_str() { "init" => ConstraintToken::Init(Context::new( ident.span(), ConstraintInit { if_needed: false }, )), "init_if_needed" => ConstraintToken::Init(Context::new( ident.span(), ConstraintInit { if_needed: true }, )), "zero" => ConstraintToken::Zeroed(Context::new(ident.span(), ConstraintZeroed {})), "mut" => ConstraintToken::Mut(Context::new( ident.span(), ConstraintMut { error: parse_optional_custom_error(&stream)?, }, )), "signer" => ConstraintToken::Signer(Context::new( ident.span(), ConstraintSigner { error: parse_optional_custom_error(&stream)?, }, )), "executable" => { ConstraintToken::Executable(Context::new(ident.span(), ConstraintExecutable {})) } "mint" => { stream.parse::()?; stream.parse::()?; let kw = stream.call(Ident::parse_any)?.to_string(); stream.parse::()?; let span = ident .span() .join(stream.span()) .unwrap_or_else(|| ident.span()); match kw.as_str() { "authority" => ConstraintToken::MintAuthority(Context::new( span, ConstraintMintAuthority { mint_auth: stream.parse()?, }, )), "freeze_authority" => ConstraintToken::MintFreezeAuthority(Context::new( span, ConstraintMintFreezeAuthority { mint_freeze_auth: stream.parse()?, }, )), "decimals" => ConstraintToken::MintDecimals(Context::new( span, ConstraintMintDecimals { decimals: stream.parse()?, }, )), _ => return Err(ParseError::new(ident.span(), "Invalid attribute")), } } "token" => { stream.parse::()?; stream.parse::()?; let kw = stream.call(Ident::parse_any)?.to_string(); stream.parse::()?; let span = ident .span() .join(stream.span()) .unwrap_or_else(|| ident.span()); match kw.as_str() { "mint" => ConstraintToken::TokenMint(Context::new( span, ConstraintTokenMint { mint: stream.parse()?, }, )), "authority" => ConstraintToken::TokenAuthority(Context::new( span, ConstraintTokenAuthority { auth: stream.parse()?, }, )), _ => return Err(ParseError::new(ident.span(), "Invalid attribute")), } } "associated_token" => { stream.parse::()?; stream.parse::()?; let kw = stream.call(Ident::parse_any)?.to_string(); stream.parse::()?; let span = ident .span() .join(stream.span()) .unwrap_or_else(|| ident.span()); match kw.as_str() { "mint" => ConstraintToken::AssociatedTokenMint(Context::new( span, ConstraintTokenMint { mint: stream.parse()?, }, )), "authority" => ConstraintToken::AssociatedTokenAuthority(Context::new( span, ConstraintTokenAuthority { auth: stream.parse()?, }, )), _ => return Err(ParseError::new(ident.span(), "Invalid attribute")), } } "bump" => { let bump = { if stream.peek(Token![=]) { stream.parse::()?; Some(stream.parse()?) } else { None } }; ConstraintToken::Bump(Context::new(ident.span(), ConstraintTokenBump { bump })) } "seeds" => { if stream.peek(Token![:]) { stream.parse::()?; stream.parse::()?; let kw = stream.call(Ident::parse_any)?.to_string(); stream.parse::()?; let span = ident .span() .join(stream.span()) .unwrap_or_else(|| ident.span()); match kw.as_str() { "program" => ConstraintToken::ProgramSeed(Context::new( span, ConstraintProgramSeed { program_seed: stream.parse()?, }, )), _ => return Err(ParseError::new(ident.span(), "Invalid attribute")), } } else { stream.parse::()?; let span = ident .span() .join(stream.span()) .unwrap_or_else(|| ident.span()); let seeds; let bracket = bracketed!(seeds in stream); ConstraintToken::Seeds(Context::new( span.join(bracket.span).unwrap_or(span), ConstraintSeeds { seeds: seeds.parse_terminated(Expr::parse)?, }, )) } } "realloc" => { if stream.peek(Token![=]) { stream.parse::()?; let span = ident .span() .join(stream.span()) .unwrap_or_else(|| ident.span()); ConstraintToken::Realloc(Context::new( span, ConstraintRealloc { space: stream.parse()?, }, )) } else { stream.parse::()?; stream.parse::()?; let kw = stream.call(Ident::parse_any)?.to_string(); stream.parse::()?; let span = ident .span() .join(stream.span()) .unwrap_or_else(|| ident.span()); match kw.as_str() { "payer" => ConstraintToken::ReallocPayer(Context::new( span, ConstraintReallocPayer { target: stream.parse()?, }, )), "zero" => ConstraintToken::ReallocZero(Context::new( span, ConstraintReallocZero { zero: stream.parse()?, }, )), _ => return Err(ParseError::new(ident.span(), "Invalid attribute. realloc::payer and realloc::zero are the only valid attributes")), } } } _ => { stream.parse::()?; let span = ident .span() .join(stream.span()) .unwrap_or_else(|| ident.span()); match kw.as_str() { "has_one" => ConstraintToken::HasOne(Context::new( span, ConstraintHasOne { join_target: stream.parse()?, error: parse_optional_custom_error(&stream)?, }, )), "owner" => ConstraintToken::Owner(Context::new( span, ConstraintOwner { owner_address: stream.parse()?, error: parse_optional_custom_error(&stream)?, }, )), "rent_exempt" => ConstraintToken::RentExempt(Context::new( span, match stream.parse::()?.to_string().as_str() { "skip" => ConstraintRentExempt::Skip, "enforce" => ConstraintRentExempt::Enforce, _ => { return Err(ParseError::new( span, "rent_exempt must be either skip or enforce", )) } }, )), "state" => ConstraintToken::State(Context::new( span, ConstraintState { program_target: stream.parse()?, }, )), "payer" => ConstraintToken::Payer(Context::new( span, ConstraintPayer { target: stream.parse()?, }, )), "space" => ConstraintToken::Space(Context::new( span, ConstraintSpace { space: stream.parse()?, }, )), "constraint" => ConstraintToken::Raw(Context::new( span, ConstraintRaw { raw: stream.parse()?, error: parse_optional_custom_error(&stream)?, }, )), "close" => ConstraintToken::Close(Context::new( span, ConstraintClose { sol_dest: stream.parse()?, }, )), "address" => ConstraintToken::Address(Context::new( span, ConstraintAddress { address: stream.parse()?, error: parse_optional_custom_error(&stream)?, }, )), _ => return Err(ParseError::new(ident.span(), "Invalid attribute")), } } }; Ok(c) } fn parse_optional_custom_error(stream: &ParseStream) -> ParseResult> { if stream.peek(Token![@]) { stream.parse::()?; stream.parse().map(Some) } else { Ok(None) } } #[derive(Default)] pub struct ConstraintGroupBuilder<'ty> { pub f_ty: Option<&'ty Ty>, pub init: Option>, pub zeroed: Option>, pub mutable: Option>, pub signer: Option>, pub has_one: Vec>, pub literal: Vec>, pub raw: Vec>, pub owner: Option>, pub rent_exempt: Option>, pub seeds: Option>, pub executable: Option>, pub state: Option>, pub payer: Option>, pub space: Option>, pub close: Option>, pub address: Option>, pub token_mint: Option>, pub token_authority: Option>, pub associated_token_mint: Option>, pub associated_token_authority: Option>, pub mint_authority: Option>, pub mint_freeze_authority: Option>, pub mint_decimals: Option>, pub bump: Option>, pub program_seed: Option>, pub realloc: Option>, pub realloc_payer: Option>, pub realloc_zero: Option>, } impl<'ty> ConstraintGroupBuilder<'ty> { pub fn new(f_ty: Option<&'ty Ty>) -> Self { Self { f_ty, init: None, zeroed: None, mutable: None, signer: None, has_one: Vec::new(), literal: Vec::new(), raw: Vec::new(), owner: None, rent_exempt: None, seeds: None, executable: None, state: None, payer: None, space: None, close: None, address: None, token_mint: None, token_authority: None, associated_token_mint: None, associated_token_authority: None, mint_authority: None, mint_freeze_authority: None, mint_decimals: None, bump: None, program_seed: None, realloc: None, realloc_payer: None, realloc_zero: None, } } pub fn build(mut self) -> ParseResult { // Init. if let Some(i) = &self.init { if cfg!(not(feature = "init-if-needed")) && i.if_needed { return Err(ParseError::new( i.span(), "init_if_needed requires that anchor-lang be imported \ with the init-if-needed cargo feature enabled. \ Carefully read the init_if_needed docs before using this feature \ to make sure you know how to protect yourself against \ re-initialization attacks.", )); } match self.mutable { Some(m) => { return Err(ParseError::new( m.span(), "mut cannot be provided with init", )) } None => self .mutable .replace(Context::new(i.span(), ConstraintMut { error: None })), }; // Rent exempt if not explicitly skipped. if self.rent_exempt.is_none() { self.rent_exempt .replace(Context::new(i.span(), ConstraintRentExempt::Enforce)); } if self.payer.is_none() { return Err(ParseError::new( i.span(), "payer must be provided when initializing an account", )); } // When initializing a non-PDA account, the account being // initialized must sign to invoke the system program's create // account instruction. if self.signer.is_none() && self.seeds.is_none() && self.associated_token_mint.is_none() { self.signer .replace(Context::new(i.span(), ConstraintSigner { error: None })); } // Assert a bump target is not given on init. if let Some(b) = &self.bump { if b.bump.is_some() { return Err(ParseError::new( b.span(), "bump targets should not be provided with init. Please use bump without a target." )); } } // TokenAccount. if let Some(token_mint) = &self.token_mint { if self.token_authority.is_none() { return Err(ParseError::new( token_mint.span(), "when initializing, token authority must be provided if token mint is", )); } } if let Some(token_authority) = &self.token_authority { if self.token_mint.is_none() { return Err(ParseError::new( token_authority.span(), "when initializing, token mint must be provided if token authority is", )); } } // Mint. if let Some(mint_decimals) = &self.mint_decimals { if self.mint_authority.is_none() { return Err(ParseError::new( mint_decimals.span(), "when initializing, mint authority must be provided if mint decimals is", )); } } if let Some(mint_authority) = &self.mint_authority { if self.mint_decimals.is_none() { return Err(ParseError::new( mint_authority.span(), "when initializing, mint decimals must be provided if mint authority is", )); } } } // Realloc. if let Some(r) = &self.realloc { if self.realloc_payer.is_none() { return Err(ParseError::new( r.span(), "realloc::payer must be provided when using realloc", )); } if self.realloc_zero.is_none() { return Err(ParseError::new( r.span(), "realloc::zero must be provided when using realloc", )); } } // Zero. if let Some(z) = &self.zeroed { match self.mutable { Some(m) => { return Err(ParseError::new( m.span(), "mut cannot be provided with zeroed", )) } None => self .mutable .replace(Context::new(z.span(), ConstraintMut { error: None })), }; // Rent exempt if not explicitly skipped. if self.rent_exempt.is_none() { self.rent_exempt .replace(Context::new(z.span(), ConstraintRentExempt::Enforce)); } } // Seeds. if let Some(i) = &self.seeds { if self.init.is_some() && self.payer.is_none() { return Err(ParseError::new( i.span(), "payer must be provided when creating a program derived address", )); } if self.bump.is_none() { return Err(ParseError::new( i.span(), "bump must be provided with seeds", )); } } // Space. if let Some(i) = &self.init { let initializing_token_program_acc = self.token_mint.is_some() || self.mint_authority.is_some() || self.token_authority.is_some() || self.associated_token_authority.is_some(); match (self.space.is_some(), initializing_token_program_acc) { (true, true) => { return Err(ParseError::new( self.space.as_ref().unwrap().span(), "space is not required for initializing an spl account", )); } (false, false) => { return Err(ParseError::new( i.span(), "space must be provided with init", )); } _ => (), } } let ConstraintGroupBuilder { f_ty: _, init, zeroed, mutable, signer, has_one, literal, raw, owner, rent_exempt, seeds, executable, state, payer, space, close, address, token_mint, token_authority, associated_token_mint, associated_token_authority, mint_authority, mint_freeze_authority, mint_decimals, bump, program_seed, realloc, realloc_payer, realloc_zero, } = self; // Converts Option> -> Option. macro_rules! into_inner { ($opt:ident) => { $opt.map(|c| c.into_inner()) }; ($opt:expr) => { $opt.map(|c| c.into_inner()) }; } // Converts Vec> - Vec. macro_rules! into_inner_vec { ($opt:ident) => { $opt.into_iter().map(|c| c.into_inner()).collect() }; } let is_init = init.is_some(); let seeds = seeds.map(|c| ConstraintSeedsGroup { is_init, seeds: c.seeds.clone(), bump: into_inner!(bump) .map(|b| b.bump) .expect("bump must be provided with seeds"), program_seed: into_inner!(program_seed).map(|id| id.program_seed), }); let associated_token = match (associated_token_mint, associated_token_authority) { (Some(mint), Some(auth)) => Some(ConstraintAssociatedToken { wallet: auth.into_inner().auth, mint: mint.into_inner().mint, }), (Some(mint), None) => return Err(ParseError::new( mint.span(), "authority must be provided to specify an associated token program derived address", )), (None, Some(auth)) => { return Err(ParseError::new( auth.span(), "mint must be provided to specify an associated token program derived address", )) } _ => None, }; if let Some(associated_token) = &associated_token { if seeds.is_some() { return Err(ParseError::new( associated_token.mint.span(), "'associated_token' constraints cannot be used with the 'seeds' constraint", )); } } let token_account = match (&token_mint, &token_authority) { (None, None) => None, _ => Some(ConstraintTokenAccountGroup { mint: token_mint.as_ref().map(|a| a.clone().into_inner().mint), authority: token_authority .as_ref() .map(|a| a.clone().into_inner().auth), }), }; let mint = match (&mint_decimals, &mint_authority, &mint_freeze_authority) { (None, None, None) => None, _ => Some(ConstraintTokenMintGroup { decimals: mint_decimals .as_ref() .map(|a| a.clone().into_inner().decimals), mint_authority: mint_authority .as_ref() .map(|a| a.clone().into_inner().mint_auth), freeze_authority: mint_freeze_authority .as_ref() .map(|a| a.clone().into_inner().mint_freeze_auth), }), }; Ok(ConstraintGroup { init: init.as_ref().map(|i| Ok(ConstraintInitGroup { if_needed: i.if_needed, seeds: seeds.clone(), payer: into_inner!(payer.clone()).unwrap().target, space: space.clone().map(|s| s.space.clone()), kind: if let Some(tm) = &token_mint { InitKind::Token { mint: tm.clone().into_inner().mint, owner: match &token_authority { Some(a) => a.clone().into_inner().auth, None => return Err(ParseError::new( tm.span(), "authority must be provided to initialize a token program derived address" )), }, } } else if let Some(at) = &associated_token { InitKind::AssociatedToken { mint: at.mint.clone(), owner: at.wallet.clone() } } else if let Some(d) = &mint_decimals { InitKind::Mint { decimals: d.clone().into_inner().decimals, owner: match &mint_authority { Some(a) => a.clone().into_inner().mint_auth, None => return Err(ParseError::new( d.span(), "authority must be provided to initialize a mint program derived address" )) }, freeze_authority: mint_freeze_authority.map(|fa| fa.into_inner().mint_freeze_auth) } } else { InitKind::Program { owner: owner.as_ref().map(|o| o.owner_address.clone()), } }, })).transpose()?, realloc: realloc.as_ref().map(|r| ConstraintReallocGroup { payer: into_inner!(realloc_payer).unwrap().target, space: r.space.clone(), zero: into_inner!(realloc_zero).unwrap().zero, }), zeroed: into_inner!(zeroed), mutable: into_inner!(mutable), signer: into_inner!(signer), has_one: into_inner_vec!(has_one), literal: into_inner_vec!(literal), raw: into_inner_vec!(raw), owner: into_inner!(owner), rent_exempt: into_inner!(rent_exempt), executable: into_inner!(executable), state: into_inner!(state), close: into_inner!(close), address: into_inner!(address), associated_token: if !is_init { associated_token } else { None }, seeds, token_account: if !is_init {token_account} else {None}, mint: if !is_init {mint} else {None}, }) } pub fn add(&mut self, c: ConstraintToken) -> ParseResult<()> { match c { ConstraintToken::Init(c) => self.add_init(c), ConstraintToken::Zeroed(c) => self.add_zeroed(c), ConstraintToken::Mut(c) => self.add_mut(c), ConstraintToken::Signer(c) => self.add_signer(c), ConstraintToken::HasOne(c) => self.add_has_one(c), ConstraintToken::Literal(c) => self.add_literal(c), ConstraintToken::Raw(c) => self.add_raw(c), ConstraintToken::Owner(c) => self.add_owner(c), ConstraintToken::RentExempt(c) => self.add_rent_exempt(c), ConstraintToken::Seeds(c) => self.add_seeds(c), ConstraintToken::Executable(c) => self.add_executable(c), ConstraintToken::State(c) => self.add_state(c), ConstraintToken::Payer(c) => self.add_payer(c), ConstraintToken::Space(c) => self.add_space(c), ConstraintToken::Close(c) => self.add_close(c), ConstraintToken::Address(c) => self.add_address(c), ConstraintToken::TokenAuthority(c) => self.add_token_authority(c), ConstraintToken::TokenMint(c) => self.add_token_mint(c), ConstraintToken::AssociatedTokenAuthority(c) => self.add_associated_token_authority(c), ConstraintToken::AssociatedTokenMint(c) => self.add_associated_token_mint(c), ConstraintToken::MintAuthority(c) => self.add_mint_authority(c), ConstraintToken::MintFreezeAuthority(c) => self.add_mint_freeze_authority(c), ConstraintToken::MintDecimals(c) => self.add_mint_decimals(c), ConstraintToken::Bump(c) => self.add_bump(c), ConstraintToken::ProgramSeed(c) => self.add_program_seed(c), ConstraintToken::Realloc(c) => self.add_realloc(c), ConstraintToken::ReallocPayer(c) => self.add_realloc_payer(c), ConstraintToken::ReallocZero(c) => self.add_realloc_zero(c), } } fn add_init(&mut self, c: Context) -> ParseResult<()> { if self.init.is_some() { return Err(ParseError::new(c.span(), "init already provided")); } if self.zeroed.is_some() { return Err(ParseError::new(c.span(), "zeroed already provided")); } if self.token_mint.is_some() { return Err(ParseError::new( c.span(), "init must be provided before token mint", )); } if self.token_authority.is_some() { return Err(ParseError::new( c.span(), "init must be provided before token authority", )); } if self.mint_authority.is_some() { return Err(ParseError::new( c.span(), "init must be provided before mint authority", )); } if self.mint_freeze_authority.is_some() { return Err(ParseError::new( c.span(), "init must be provided before mint freeze authority", )); } if self.mint_decimals.is_some() { return Err(ParseError::new( c.span(), "init must be provided before mint decimals", )); } if self.associated_token_mint.is_some() { return Err(ParseError::new( c.span(), "init must be provided before associated token mint", )); } if self.associated_token_authority.is_some() { return Err(ParseError::new( c.span(), "init must be provided before associated token authority", )); } self.init.replace(c); Ok(()) } fn add_zeroed(&mut self, c: Context) -> ParseResult<()> { if self.zeroed.is_some() { return Err(ParseError::new(c.span(), "zeroed already provided")); } if self.init.is_some() { return Err(ParseError::new(c.span(), "init already provided")); } self.zeroed.replace(c); Ok(()) } fn add_realloc(&mut self, c: Context) -> ParseResult<()> { if !matches!(self.f_ty, Some(Ty::Account(_))) && !matches!(self.f_ty, Some(Ty::AccountLoader(_))) { return Err(ParseError::new( c.span(), "realloc must be on an Account or AccountLoader", )); } if self.mutable.is_none() { return Err(ParseError::new( c.span(), "mut must be provided before realloc", )); } if self.realloc.is_some() { return Err(ParseError::new(c.span(), "realloc already provided")); } self.realloc.replace(c); Ok(()) } fn add_realloc_payer(&mut self, c: Context) -> ParseResult<()> { if self.realloc.is_none() { return Err(ParseError::new( c.span(), "realloc must be provided before realloc::payer", )); } if self.realloc_payer.is_some() { return Err(ParseError::new(c.span(), "realloc::payer already provided")); } self.realloc_payer.replace(c); Ok(()) } fn add_realloc_zero(&mut self, c: Context) -> ParseResult<()> { if self.realloc.is_none() { return Err(ParseError::new( c.span(), "realloc must be provided before realloc::zero", )); } if self.realloc_zero.is_some() { return Err(ParseError::new(c.span(), "realloc::zero already provided")); } self.realloc_zero.replace(c); Ok(()) } fn add_close(&mut self, c: Context) -> ParseResult<()> { if !matches!(self.f_ty, Some(Ty::ProgramAccount(_))) && !matches!(self.f_ty, Some(Ty::Account(_))) && !matches!(self.f_ty, Some(Ty::Loader(_))) && !matches!(self.f_ty, Some(Ty::AccountLoader(_))) { return Err(ParseError::new( c.span(), "close must be on an Account, ProgramAccount, or Loader", )); } if self.mutable.is_none() { return Err(ParseError::new( c.span(), "mut must be provided before close", )); } if self.close.is_some() { return Err(ParseError::new(c.span(), "close already provided")); } self.close.replace(c); Ok(()) } fn add_address(&mut self, c: Context) -> ParseResult<()> { if self.address.is_some() { return Err(ParseError::new(c.span(), "address already provided")); } self.address.replace(c); Ok(()) } fn add_token_mint(&mut self, c: Context) -> ParseResult<()> { if self.token_mint.is_some() { return Err(ParseError::new(c.span(), "token mint already provided")); } if self.associated_token_mint.is_some() { return Err(ParseError::new( c.span(), "associated token mint already provided", )); } self.token_mint.replace(c); Ok(()) } fn add_associated_token_mint(&mut self, c: Context) -> ParseResult<()> { if self.associated_token_mint.is_some() { return Err(ParseError::new( c.span(), "associated token mint already provided", )); } if self.token_mint.is_some() { return Err(ParseError::new(c.span(), "token mint already provided")); } self.associated_token_mint.replace(c); Ok(()) } fn add_bump(&mut self, c: Context) -> ParseResult<()> { if self.bump.is_some() { return Err(ParseError::new(c.span(), "bump already provided")); } if self.seeds.is_none() { return Err(ParseError::new( c.span(), "seeds must be provided before bump", )); } self.bump.replace(c); Ok(()) } fn add_program_seed(&mut self, c: Context) -> ParseResult<()> { if self.program_seed.is_some() { return Err(ParseError::new(c.span(), "seeds::program already provided")); } if self.seeds.is_none() { return Err(ParseError::new( c.span(), "seeds must be provided before seeds::program", )); } if let Some(ref init) = self.init { if init.if_needed { return Err(ParseError::new( c.span(), "seeds::program cannot be used with init_if_needed", )); } else { return Err(ParseError::new( c.span(), "seeds::program cannot be used with init", )); } } self.program_seed.replace(c); Ok(()) } fn add_token_authority(&mut self, c: Context) -> ParseResult<()> { if self.token_authority.is_some() { return Err(ParseError::new( c.span(), "token authority already provided", )); } self.token_authority.replace(c); Ok(()) } fn add_associated_token_authority( &mut self, c: Context, ) -> ParseResult<()> { if self.associated_token_authority.is_some() { return Err(ParseError::new( c.span(), "associated token authority already provided", )); } if self.token_authority.is_some() { return Err(ParseError::new( c.span(), "token authority already provided", )); } self.associated_token_authority.replace(c); Ok(()) } fn add_mint_authority(&mut self, c: Context) -> ParseResult<()> { if self.mint_authority.is_some() { return Err(ParseError::new(c.span(), "mint authority already provided")); } self.mint_authority.replace(c); Ok(()) } fn add_mint_freeze_authority( &mut self, c: Context, ) -> ParseResult<()> { if self.mint_freeze_authority.is_some() { return Err(ParseError::new( c.span(), "mint freeze_authority already provided", )); } self.mint_freeze_authority.replace(c); Ok(()) } fn add_mint_decimals(&mut self, c: Context) -> ParseResult<()> { if self.mint_decimals.is_some() { return Err(ParseError::new(c.span(), "mint decimals already provided")); } self.mint_decimals.replace(c); Ok(()) } fn add_mut(&mut self, c: Context) -> ParseResult<()> { if self.mutable.is_some() { return Err(ParseError::new(c.span(), "mut already provided")); } self.mutable.replace(c); Ok(()) } fn add_signer(&mut self, c: Context) -> ParseResult<()> { if self.signer.is_some() { return Err(ParseError::new(c.span(), "signer already provided")); } self.signer.replace(c); Ok(()) } fn add_has_one(&mut self, c: Context) -> ParseResult<()> { if self .has_one .iter() .filter(|item| item.join_target == c.join_target) .count() > 0 { return Err(ParseError::new(c.span(), "has_one target already provided")); } self.has_one.push(c); Ok(()) } fn add_literal(&mut self, c: Context) -> ParseResult<()> { self.literal.push(c); Ok(()) } fn add_raw(&mut self, c: Context) -> ParseResult<()> { self.raw.push(c); Ok(()) } fn add_owner(&mut self, c: Context) -> ParseResult<()> { if self.owner.is_some() { return Err(ParseError::new(c.span(), "owner already provided")); } self.owner.replace(c); Ok(()) } fn add_rent_exempt(&mut self, c: Context) -> ParseResult<()> { if self.rent_exempt.is_some() { return Err(ParseError::new(c.span(), "rent already provided")); } self.rent_exempt.replace(c); Ok(()) } fn add_seeds(&mut self, c: Context) -> ParseResult<()> { if self.seeds.is_some() { return Err(ParseError::new(c.span(), "seeds already provided")); } self.seeds.replace(c); Ok(()) } fn add_executable(&mut self, c: Context) -> ParseResult<()> { if self.executable.is_some() { return Err(ParseError::new(c.span(), "executable already provided")); } self.executable.replace(c); Ok(()) } fn add_state(&mut self, c: Context) -> ParseResult<()> { if self.state.is_some() { return Err(ParseError::new(c.span(), "state already provided")); } self.state.replace(c); Ok(()) } fn add_payer(&mut self, c: Context) -> ParseResult<()> { if self.init.is_none() { return Err(ParseError::new( c.span(), "init must be provided before payer", )); } if self.payer.is_some() { return Err(ParseError::new(c.span(), "payer already provided")); } self.payer.replace(c); Ok(()) } fn add_space(&mut self, c: Context) -> ParseResult<()> { if self.init.is_none() { return Err(ParseError::new( c.span(), "init must be provided before space", )); } if self.space.is_some() { return Err(ParseError::new(c.span(), "space already provided")); } self.space.replace(c); Ok(()) } }