1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3pub mod codegen;
4pub mod parser;
5
6#[cfg(feature = "idl-build")]
7pub mod idl;
8
9#[cfg(feature = "hash")]
10pub mod hash;
11#[cfg(not(feature = "hash"))]
12pub(crate) mod hash;
13
14use {
15 codegen::{accounts as accounts_codegen, program as program_codegen},
16 parser::{accounts as accounts_parser, program as program_parser},
17 proc_macro2::{Span, TokenStream},
18 quote::{quote, ToTokens},
19 std::{collections::HashMap, ops::Deref},
20 syn::{
21 ext::IdentExt,
22 parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult},
23 parse_quote,
24 punctuated::Punctuated,
25 spanned::Spanned,
26 token::Comma,
27 Attribute, Expr, ExprLit, Generics, Ident, ItemEnum, ItemFn, ItemMod, ItemStruct, Lit,
28 LitInt, PatType, Token, Type, TypePath,
29 },
30};
31
32#[cfg(feature = "idl-build")]
34pub(crate) type AnyResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
35
36#[derive(Debug)]
37pub struct Program {
38 pub ixs: Vec<Ix>,
39 pub name: Ident,
40 pub docs: Option<Vec<String>>,
41 pub program_mod: ItemMod,
42 pub fallback_fn: Option<FallbackFn>,
43 pub program_args: Option<ProgramArgs>,
44}
45
46impl Parse for Program {
47 fn parse(input: ParseStream) -> ParseResult<Self> {
48 let program_mod = <ItemMod as Parse>::parse(input)?;
49 program_parser::parse(program_mod)
50 }
51}
52
53impl From<&Program> for TokenStream {
54 fn from(program: &Program) -> Self {
55 program_codegen::generate(program)
56 }
57}
58
59impl ToTokens for Program {
60 fn to_tokens(&self, tokens: &mut TokenStream) {
61 tokens.extend::<TokenStream>(self.into());
62 }
63}
64
65#[derive(Debug, Default)]
66pub struct ProgramArgs {
67 legacy_idl: bool,
68}
69
70impl Parse for ProgramArgs {
71 fn parse(input: ParseStream) -> ParseResult<Self> {
72 let mut parsed = Self::default();
73 let args = input.parse_terminated(Ident::parse, Token![,])?;
74
75 for arg in args {
76 match arg.to_string().as_str() {
77 "legacy_idl" => {
78 if parsed.legacy_idl {
79 return Err(syn::Error::new(
80 arg.span(),
81 "Duplicate `legacy_idl` argument",
82 ));
83 }
84 parsed.legacy_idl = true;
85 }
86 name => {
87 return Err(syn::Error::new(
88 arg.span(),
89 format!("Invalid argument `{name}`. Expected one of: `legacy_idl`"),
90 ));
91 }
92 }
93 }
94
95 Ok(parsed)
96 }
97}
98
99#[derive(Debug)]
100pub struct Ix {
101 pub raw_method: ItemFn,
102 pub ident: Ident,
103 pub docs: Option<Vec<String>>,
104 pub cfgs: Vec<Attribute>,
105 pub args: Vec<IxArg>,
106 pub returns: IxReturn,
107 pub anchor_ident: Ident,
109 pub overrides: Option<Overrides>,
111}
112
113#[derive(Debug, Default)]
115pub struct Overrides {
116 pub discriminator: Option<Box<Expr>>,
119}
120
121impl Parse for Overrides {
122 fn parse(input: ParseStream) -> ParseResult<Self> {
123 let mut attr = Self::default();
124 let args = input.parse_terminated(NamedArg::parse, Token![,])?;
125 for arg in args {
126 match arg.name.to_string().as_str() {
127 "discriminator" => {
128 let value = match arg.value {
129 Expr::Lit(ExprLit {
131 lit: lit @ Lit::Int(_),
132 ..
133 }) => {
134 parse_quote!(&[#lit])
135 }
136 Expr::Array(arr) => {
138 parse_quote!(&#arr)
139 }
140 expr => expr,
141 };
142 attr.discriminator.replace(Box::new(value))
143 }
144 name => {
145 return Err(ParseError::new(
146 arg.name.span(),
147 format!(
148 "Invalid argument `{}`. Expected one of: `discriminator`",
149 name
150 ),
151 ));
152 }
153 };
154 }
155
156 Ok(attr)
157 }
158}
159
160struct NamedArg {
161 name: Ident,
162 #[allow(dead_code)]
163 eq_token: Token![=],
164 value: Expr,
165}
166
167impl Parse for NamedArg {
168 fn parse(input: ParseStream) -> ParseResult<Self> {
169 Ok(Self {
170 name: input.parse()?,
171 eq_token: input.parse()?,
172 value: input.parse()?,
173 })
174 }
175}
176
177#[derive(Debug)]
178pub struct IxArg {
179 pub name: Ident,
180 pub docs: Option<Vec<String>>,
181 pub raw_arg: PatType,
182}
183
184#[derive(Debug)]
185pub struct IxReturn {
186 pub ty: Type,
187}
188
189#[derive(Debug)]
190pub struct FallbackFn {
191 raw_method: ItemFn,
192}
193
194#[derive(Debug)]
195pub struct AccountsStruct {
196 pub ident: Ident,
198 pub generics: Generics,
200 pub fields: Vec<AccountField>,
202 instruction_api: Option<Punctuated<syn::FnArg, Comma>>,
204}
205
206impl Parse for AccountsStruct {
207 fn parse(input: ParseStream) -> ParseResult<Self> {
208 let strct = <ItemStruct as Parse>::parse(input)?;
209 accounts_parser::parse(&strct)
210 }
211}
212
213impl From<&AccountsStruct> for TokenStream {
214 fn from(accounts: &AccountsStruct) -> Self {
215 accounts_codegen::generate(accounts)
216 }
217}
218
219impl ToTokens for AccountsStruct {
220 fn to_tokens(&self, tokens: &mut TokenStream) {
221 tokens.extend::<TokenStream>(self.into());
222 }
223}
224
225impl AccountsStruct {
226 pub fn new(
227 strct: ItemStruct,
228 fields: Vec<AccountField>,
229 instruction_api: Option<Punctuated<syn::FnArg, Comma>>,
230 ) -> Self {
231 let ident = strct.ident.clone();
232 let generics = strct.generics;
233 Self {
234 ident,
235 generics,
236 fields,
237 instruction_api,
238 }
239 }
240
241 pub fn instruction_args(&self) -> Option<HashMap<String, String>> {
245 self.instruction_api.as_ref().map(|instruction_api| {
246 instruction_api
247 .iter()
248 .map(|expr| {
249 let arg = parser::tts_to_string(expr);
250 let components: Vec<&str> = arg.split(" : ").collect();
251 assert!(components.len() == 2);
252 #[allow(
253 clippy::indexing_slicing,
254 reason = "len == 2 asserted immediately above"
255 )]
256 let result = (components[0].to_string(), components[1].to_string());
257 result
258 })
259 .collect()
260 })
261 }
262
263 pub fn field_names(&self) -> Vec<String> {
264 self.fields
265 .iter()
266 .map(|field| field.ident().to_string())
267 .collect()
268 }
269
270 pub fn has_optional(&self) -> bool {
271 for field in &self.fields {
272 if let AccountField::Field(field) = field {
273 if field.is_optional {
274 return true;
275 }
276 }
277 }
278 false
279 }
280
281 pub fn is_field_optional<T: quote::ToTokens>(&self, field: &T) -> bool {
282 let matching_field = self
283 .fields
284 .iter()
285 .find(|f| *f.ident() == parser::tts_to_string(field));
286 if let Some(matching_field) = matching_field {
287 matching_field.is_optional()
288 } else {
289 false
290 }
291 }
292}
293
294#[allow(clippy::large_enum_variant)]
295#[derive(Debug)]
296pub enum AccountField {
297 Field(Field),
298 CompositeField(CompositeField),
299}
300
301impl AccountField {
302 fn ident(&self) -> &Ident {
303 match self {
304 AccountField::Field(field) => &field.ident,
305 AccountField::CompositeField(c_field) => &c_field.ident,
306 }
307 }
308
309 fn is_optional(&self) -> bool {
310 match self {
311 AccountField::Field(field) => field.is_optional,
312 AccountField::CompositeField(_) => false,
313 }
314 }
315
316 pub fn ty_name(&self) -> Option<String> {
317 let qualified_ty_name = match self {
318 AccountField::Field(field) => match &field.ty {
319 Ty::Account(account) => Some(parser::tts_to_string(&account.account_type_path)),
320 Ty::LazyAccount(account) => Some(parser::tts_to_string(&account.account_type_path)),
321 _ => None,
322 },
323 AccountField::CompositeField(field) => Some(field.symbol.clone()),
324 };
325
326 qualified_ty_name.map(|name| match name.rsplit_once(" :: ") {
327 Some((_prefix, suffix)) => suffix.to_string(),
328 None => name,
329 })
330 }
331}
332
333#[derive(Debug)]
334pub struct Field {
335 pub ident: Ident,
336 pub constraints: ConstraintGroup,
337 pub ty: Ty,
338 pub is_optional: bool,
339 pub ty_span: Span,
340 pub docs: Option<Vec<String>>,
342}
343
344impl Field {
345 pub fn typed_ident(&self) -> proc_macro2::TokenStream {
346 let name = &self.ident;
347 let ty_decl = self.ty_decl(false);
348 quote! {
349 #name: #ty_decl
350 }
351 }
352
353 pub fn ty_decl(&self, ignore_option: bool) -> proc_macro2::TokenStream {
354 let account_ty = self.account_ty();
355 let container_ty = self.container_ty();
356 let inner_ty = match &self.ty {
357 Ty::AccountInfo => quote! {
358 AccountInfo
359 },
360 Ty::UncheckedAccount => quote! {
361 UncheckedAccount
362 },
363 Ty::Signer => quote! {
364 Signer
365 },
366 Ty::ProgramData => quote! {
367 ProgramData
368 },
369 Ty::SystemAccount => quote! {
370 SystemAccount
371 },
372 Ty::Account(AccountTy { boxed, .. })
373 | Ty::InterfaceAccount(InterfaceAccountTy { boxed, .. }) => {
374 if *boxed {
375 quote! {
376 Box<#container_ty<#account_ty>>
377 }
378 } else {
379 quote! {
380 #container_ty<#account_ty>
381 }
382 }
383 }
384 Ty::Sysvar(ty) => {
385 let account = match ty {
386 SysvarTy::Clock => quote! {Clock},
387 SysvarTy::Rent => quote! {Rent},
388 SysvarTy::EpochSchedule => quote! {EpochSchedule},
389 SysvarTy::Fees => quote! {Fees},
390 SysvarTy::RecentBlockhashes => quote! {RecentBlockhashes},
391 SysvarTy::SlotHashes => quote! {SlotHashes},
392 SysvarTy::SlotHistory => quote! {SlotHistory},
393 SysvarTy::StakeHistory => quote! {StakeHistory},
394 SysvarTy::Instructions => quote! {Instructions},
395 SysvarTy::Rewards => quote! {Rewards},
396 };
397 quote! {
398 Sysvar<#account>
399 }
400 }
401 Ty::Program(ty) => {
402 let program = &ty.account_type_path;
403 let program_str = quote!(#program).to_string();
405 if program_str == "__SolanaProgramUnitType" {
406 quote! {
407 #container_ty<'info>
408 }
409 } else {
410 quote! {
411 #container_ty<'info, #program>
412 }
413 }
414 }
415 Ty::Migration(ty) => {
416 let from = &ty.from_type_path;
417 let to = &ty.to_type_path;
418 quote! {
419 #container_ty<'info, #from, #to>
420 }
421 }
422 _ => quote! {
423 #container_ty<#account_ty>
424 },
425 };
426 if self.is_optional && !ignore_option {
427 quote! {
428 Option<#inner_ty>
429 }
430 } else {
431 quote! {
432 #inner_ty
433 }
434 }
435 }
436
437 pub fn from_account_info(
440 &self,
441 kind: Option<&InitKind>,
442 checked: bool,
443 ) -> proc_macro2::TokenStream {
444 let field = &self.ident;
445 let field_str = field.to_string();
446 let container_ty = self.container_ty();
447 let program_id = crate::codegen::private_ident("__program_id");
448 let owner_addr = match &kind {
449 None => quote! { #program_id },
450 Some(InitKind::Program { .. }) => quote! {
451 #program_id
452 },
453 _ => quote! {
454 &anchor_spl::token::ID
455 },
456 };
457 match &self.ty {
458 Ty::AccountInfo => quote! { #field.to_account_info() },
459 Ty::UncheckedAccount => {
460 quote! { UncheckedAccount::try_from(&#field) }
461 }
462 Ty::Account(AccountTy { boxed, .. })
463 | Ty::InterfaceAccount(InterfaceAccountTy { boxed, .. }) => {
464 let stream = if checked {
465 quote! {
466 match #container_ty::try_from(&#field) {
467 Ok(val) => val,
468 Err(e) => return Err(e.with_account_name(#field_str))
469 }
470 }
471 } else {
472 quote! {
473 match #container_ty::try_from_unchecked(&#field) {
474 Ok(val) => val,
475 Err(e) => return Err(e.with_account_name(#field_str))
476 }
477 }
478 };
479 if *boxed {
480 quote! {
481 Box::new(#stream)
482 }
483 } else {
484 stream
485 }
486 }
487 Ty::LazyAccount(_) => {
488 if checked {
489 quote! {
490 match #container_ty::try_from(&#field) {
491 Ok(val) => val,
492 Err(e) => return Err(e.with_account_name(#field_str))
493 }
494 }
495 } else {
496 quote! {
497 match #container_ty::try_from_unchecked(&#field) {
498 Ok(val) => val,
499 Err(e) => return Err(e.with_account_name(#field_str))
500 }
501 }
502 }
503 }
504 Ty::AccountLoader(_) => {
505 if checked {
506 quote! {
507 match #container_ty::try_from(&#field) {
508 Ok(val) => val,
509 Err(e) => return Err(e.with_account_name(#field_str))
510 }
511 }
512 } else {
513 quote! {
514 match #container_ty::try_from_unchecked(#owner_addr, &#field) {
515 Ok(val) => val,
516 Err(e) => return Err(e.with_account_name(#field_str))
517 }
518 }
519 }
520 }
521 _ => {
522 if checked {
523 quote! {
524 match #container_ty::try_from(#owner_addr, &#field) {
525 Ok(val) => val,
526 Err(e) => return Err(e.with_account_name(#field_str))
527 }
528 }
529 } else {
530 quote! {
531 match #container_ty::try_from_unchecked(#owner_addr, &#field) {
532 Ok(val) => val,
533 Err(e) => return Err(e.with_account_name(#field_str))
534 }
535 }
536 }
537 }
538 }
539 }
540
541 pub fn container_ty(&self) -> proc_macro2::TokenStream {
542 match &self.ty {
543 Ty::Account(_) => quote! {
544 anchor_lang::accounts::account::Account
545 },
546 Ty::LazyAccount(_) => quote! {
547 anchor_lang::accounts::lazy_account::LazyAccount
548 },
549 Ty::AccountLoader(_) => quote! {
550 anchor_lang::accounts::account_loader::AccountLoader
551 },
552 Ty::Migration(_) => quote! {
553 anchor_lang::accounts::migration::Migration
554 },
555 Ty::Sysvar(_) => quote! { anchor_lang::accounts::sysvar::Sysvar },
556 Ty::Program(_) => quote! { anchor_lang::accounts::program::Program },
557 Ty::Interface(_) => quote! { anchor_lang::accounts::interface::Interface },
558 Ty::InterfaceAccount(_) => {
559 quote! { anchor_lang::accounts::interface_account::InterfaceAccount }
560 }
561 Ty::AccountInfo => quote! {},
562 Ty::UncheckedAccount => quote! {},
563 Ty::Signer => quote! {},
564 Ty::SystemAccount => quote! {},
565 Ty::ProgramData => quote! {},
566 }
567 }
568
569 pub fn account_ty(&self) -> proc_macro2::TokenStream {
571 match &self.ty {
572 Ty::AccountInfo => quote! {
573 AccountInfo
574 },
575 Ty::UncheckedAccount => quote! {
576 UncheckedAccount
577 },
578 Ty::Signer => quote! {
579 Signer
580 },
581 Ty::SystemAccount => quote! {
582 SystemAccount
583 },
584 Ty::ProgramData => quote! {
585 ProgramData
586 },
587 Ty::Account(ty) => {
588 let ident = &ty.account_type_path;
589 quote! {
590 #ident
591 }
592 }
593 Ty::LazyAccount(ty) => {
594 let ident = &ty.account_type_path;
595 quote! {
596 #ident
597 }
598 }
599 Ty::InterfaceAccount(ty) => {
600 let ident = &ty.account_type_path;
601 quote! {
602 #ident
603 }
604 }
605 Ty::AccountLoader(ty) => {
606 let ident = &ty.account_type_path;
607 quote! {
608 #ident
609 }
610 }
611 Ty::Migration(ty) => {
612 let from = &ty.from_type_path;
614 quote! {
615 #from
616 }
617 }
618 Ty::Sysvar(ty) => match ty {
619 SysvarTy::Clock => quote! {Clock},
620 SysvarTy::Rent => quote! {Rent},
621 SysvarTy::EpochSchedule => quote! {EpochSchedule},
622 SysvarTy::Fees => quote! {Fees},
623 SysvarTy::RecentBlockhashes => quote! {RecentBlockhashes},
624 SysvarTy::SlotHashes => quote! {SlotHashes},
625 SysvarTy::SlotHistory => quote! {SlotHistory},
626 SysvarTy::StakeHistory => quote! {StakeHistory},
627 SysvarTy::Instructions => quote! {Instructions},
628 SysvarTy::Rewards => quote! {Rewards},
629 },
630 Ty::Program(ty) => {
631 let program = &ty.account_type_path;
632 let program_str = quote!(#program).to_string();
634 if program_str == "__SolanaProgramUnitType" {
635 quote! {}
636 } else {
637 quote! {
638 #program
639 }
640 }
641 }
642 Ty::Interface(ty) => {
643 let program = &ty.account_type_path;
644 quote! {
645 #program
646 }
647 }
648 }
649 }
650}
651
652#[derive(Debug)]
653pub struct CompositeField {
654 pub ident: Ident,
655 pub constraints: ConstraintGroup,
656 pub symbol: String,
657 pub raw_field: syn::Field,
658 pub docs: Option<Vec<String>>,
660}
661
662#[derive(Debug, PartialEq, Eq)]
664pub enum Ty {
665 AccountInfo,
666 UncheckedAccount,
667 AccountLoader(AccountLoaderTy),
668 Sysvar(SysvarTy),
669 Account(AccountTy),
670 LazyAccount(LazyAccountTy),
671 Migration(MigrationTy),
672 Program(ProgramTy),
673 Interface(InterfaceTy),
674 InterfaceAccount(InterfaceAccountTy),
675 Signer,
676 SystemAccount,
677 ProgramData,
678}
679
680#[derive(Debug, PartialEq, Eq)]
681pub enum SysvarTy {
682 Clock,
683 Rent,
684 EpochSchedule,
685 Fees,
686 RecentBlockhashes,
687 SlotHashes,
688 SlotHistory,
689 StakeHistory,
690 Instructions,
691 Rewards,
692}
693
694#[derive(Debug, PartialEq, Eq)]
695pub struct AccountLoaderTy {
696 pub account_type_path: TypePath,
698}
699
700#[derive(Debug, PartialEq, Eq)]
701pub struct AccountTy {
702 pub account_type_path: TypePath,
704 pub boxed: bool,
706}
707
708#[derive(Debug, PartialEq, Eq)]
709pub struct LazyAccountTy {
710 pub account_type_path: TypePath,
712}
713
714#[derive(Debug, PartialEq, Eq)]
715pub struct MigrationTy {
716 pub from_type_path: TypePath,
718 pub to_type_path: TypePath,
719}
720
721#[derive(Debug, PartialEq, Eq)]
722pub struct InterfaceAccountTy {
723 pub account_type_path: TypePath,
725 pub boxed: bool,
727}
728
729#[derive(Debug, PartialEq, Eq)]
730pub struct ProgramTy {
731 pub account_type_path: TypePath,
733}
734
735#[derive(Debug, PartialEq, Eq)]
736pub struct InterfaceTy {
737 pub account_type_path: TypePath,
739}
740
741#[derive(Debug)]
742pub struct Error {
743 pub name: String,
744 pub raw_enum: ItemEnum,
745 pub ident: Ident,
746 pub codes: Vec<ErrorCode>,
747 pub args: Option<ErrorArgs>,
748}
749
750#[derive(Debug)]
751pub struct ErrorArgs {
752 pub offset: LitInt,
753}
754
755impl Parse for ErrorArgs {
756 fn parse(stream: ParseStream) -> ParseResult<Self> {
757 let offset_span = stream.span();
758 let offset = stream.call(Ident::parse_any)?;
759 if offset.to_string().as_str() != "offset" {
760 return Err(ParseError::new(offset_span, "expected keyword offset"));
761 }
762 stream.parse::<Token![=]>()?;
763 let offset: LitInt = stream.parse()?;
764 Ok(ErrorArgs { offset })
765 }
766}
767
768#[derive(Debug)]
769pub struct ErrorCode {
770 pub id: u32,
771 pub ident: Ident,
772 pub msg: Option<String>,
773}
774
775#[derive(Debug, Default, Clone)]
777pub struct ConstraintGroup {
778 pub init: Option<ConstraintInitGroup>,
779 pub zeroed: Option<ConstraintZeroed>,
780 pub mutable: Option<ConstraintMut>,
781 pub dup: Option<ConstraintDup>,
782 pub signer: Option<ConstraintSigner>,
783 pub owner: Option<ConstraintOwner>,
784 pub rent_exempt: Option<ConstraintRentExempt>,
785 pub seeds: Option<ConstraintSeedsGroup>,
786 pub executable: Option<ConstraintExecutable>,
787 pub has_one: Vec<ConstraintHasOne>,
788 pub raw: Vec<ConstraintRaw>,
789 pub close: Option<ConstraintClose>,
790 pub address: Option<ConstraintAddress>,
791 pub associated_token: Option<ConstraintAssociatedToken>,
792 pub token_account: Option<ConstraintTokenAccountGroup>,
793 pub mint: Option<ConstraintTokenMintGroup>,
794 pub realloc: Option<ConstraintReallocGroup>,
795}
796
797impl ConstraintGroup {
798 pub fn is_zeroed(&self) -> bool {
799 self.zeroed.is_some()
800 }
801
802 pub fn is_mutable(&self) -> bool {
803 self.mutable.is_some()
804 }
805
806 pub fn is_dup(&self) -> bool {
807 self.dup.is_some()
808 }
809
810 pub fn is_pure_init(&self) -> bool {
813 matches!(&self.init, Some(c) if !c.if_needed)
814 }
815
816 pub fn is_signer(&self) -> bool {
817 self.signer.is_some()
818 }
819
820 pub fn is_close(&self) -> bool {
821 self.close.is_some()
822 }
823}
824
825#[allow(clippy::large_enum_variant)]
829#[derive(Debug)]
830pub enum Constraint {
831 Init(ConstraintInitGroup),
832 Zeroed(ConstraintZeroed),
833 Mut(ConstraintMut),
834 Dup(ConstraintDup),
835 Signer(ConstraintSigner),
836 HasOne(ConstraintHasOne),
837 Raw(ConstraintRaw),
838 Owner(ConstraintOwner),
839 RentExempt(ConstraintRentExempt),
840 Seeds(ConstraintSeedsGroup),
841 AssociatedToken(ConstraintAssociatedToken),
842 Executable(ConstraintExecutable),
843 Close(ConstraintClose),
844 Address(ConstraintAddress),
845 TokenAccount(ConstraintTokenAccountGroup),
846 Mint(ConstraintTokenMintGroup),
847 Realloc(ConstraintReallocGroup),
848}
849
850#[allow(clippy::large_enum_variant)]
852#[derive(Debug)]
853pub enum ConstraintToken {
854 Init(Context<ConstraintInit>),
855 Zeroed(Context<ConstraintZeroed>),
856 Mut(Context<ConstraintMut>),
857 Dup(Context<ConstraintDup>),
858 Signer(Context<ConstraintSigner>),
859 HasOne(Context<ConstraintHasOne>),
860 Raw(Context<ConstraintRaw>),
861 Owner(Context<ConstraintOwner>),
862 RentExempt(Context<ConstraintRentExempt>),
863 Seeds(Context<ConstraintSeeds>),
864 Executable(Context<ConstraintExecutable>),
865 Close(Context<ConstraintClose>),
866 Payer(Context<ConstraintPayer>),
867 Space(Context<ConstraintSpace>),
868 Address(Context<ConstraintAddress>),
869 TokenMint(Context<ConstraintTokenMint>),
870 TokenAuthority(Context<ConstraintTokenAuthority>),
871 TokenTokenProgram(Context<ConstraintTokenProgram>),
872 AssociatedTokenMint(Context<ConstraintTokenMint>),
873 AssociatedTokenAuthority(Context<ConstraintTokenAuthority>),
874 AssociatedTokenTokenProgram(Context<ConstraintTokenProgram>),
875 MintAuthority(Context<ConstraintMintAuthority>),
876 MintFreezeAuthority(Context<ConstraintMintFreezeAuthority>),
877 MintDecimals(Context<ConstraintMintDecimals>),
878 MintTokenProgram(Context<ConstraintTokenProgram>),
879 Bump(Context<ConstraintTokenBump>),
880 ProgramSeed(Context<ConstraintProgramSeed>),
881 Realloc(Context<ConstraintRealloc>),
882 ReallocPayer(Context<ConstraintReallocPayer>),
883 ReallocZero(Context<ConstraintReallocZero>),
884 ExtensionGroupPointerAuthority(Context<ConstraintExtensionAuthority>),
886 ExtensionGroupPointerGroupAddress(Context<ConstraintExtensionGroupPointerGroupAddress>),
887 ExtensionGroupMemberPointerAuthority(Context<ConstraintExtensionAuthority>),
888 ExtensionGroupMemberPointerMemberAddress(
889 Context<ConstraintExtensionGroupMemberPointerMemberAddress>,
890 ),
891 ExtensionMetadataPointerAuthority(Context<ConstraintExtensionAuthority>),
892 ExtensionMetadataPointerMetadataAddress(
893 Context<ConstraintExtensionMetadataPointerMetadataAddress>,
894 ),
895 ExtensionCloseAuthority(Context<ConstraintExtensionAuthority>),
896 ExtensionTokenHookAuthority(Context<ConstraintExtensionAuthority>),
897 ExtensionTokenHookProgramId(Context<ConstraintExtensionTokenHookProgramId>),
898 ExtensionPermanentDelegate(Context<ConstraintExtensionPermanentDelegate>),
899 ExtensionPausableAuthority(Context<ConstraintExtensionAuthority>),
900}
901
902impl Parse for ConstraintToken {
903 fn parse(stream: ParseStream) -> ParseResult<Self> {
904 accounts_parser::constraints::parse_token(stream)
905 }
906}
907
908#[derive(Debug, Clone)]
909pub struct ConstraintInit {
910 pub if_needed: bool,
911}
912
913#[derive(Debug, Clone)]
914pub struct ConstraintInitIfNeeded {}
915
916#[derive(Debug, Clone)]
917pub struct ConstraintZeroed {}
918
919#[derive(Debug, Clone)]
920pub struct ConstraintMut {
921 pub error: Option<Expr>,
922}
923
924#[derive(Debug, Clone)]
925pub struct ConstraintDup {}
926
927#[derive(Debug, Clone)]
928pub struct ConstraintReallocGroup {
929 pub payer: Expr,
930 pub space: Expr,
931 pub zero: Expr,
932}
933
934#[derive(Debug, Clone)]
935pub struct ConstraintRealloc {
936 pub space: Expr,
937}
938
939#[derive(Debug, Clone)]
940pub struct ConstraintReallocPayer {
941 pub target: Expr,
942}
943
944#[derive(Debug, Clone)]
945pub struct ConstraintReallocZero {
946 pub zero: Expr,
947}
948
949#[derive(Debug, Clone)]
950pub struct ConstraintSigner {
951 pub error: Option<Expr>,
952}
953
954#[derive(Debug, Clone)]
955pub struct ConstraintHasOne {
956 pub join_target: Expr,
957 pub error: Option<Expr>,
958}
959
960#[derive(Debug, Clone)]
961pub struct ConstraintRaw {
962 pub raw: Expr,
963 pub error: Option<Expr>,
964}
965
966#[derive(Debug, Clone)]
967pub struct ConstraintOwner {
968 pub owner_address: Expr,
969 pub error: Option<Expr>,
970}
971
972#[derive(Debug, Clone)]
973pub struct ConstraintAddress {
974 pub address: Expr,
975 pub error: Option<Expr>,
976}
977
978#[derive(Debug, Clone)]
979pub enum ConstraintRentExempt {
980 Enforce,
981 Skip,
982}
983
984#[derive(Debug, Clone)]
985pub struct ConstraintInitGroup {
986 pub if_needed: bool,
987 pub seeds: Option<ConstraintSeedsGroup>,
988 pub payer: Expr,
989 pub space: Option<Expr>,
990 pub kind: InitKind,
991}
992
993#[derive(Debug, Clone)]
996pub enum SeedsExpr {
997 List(Punctuated<Expr, Token![,]>),
999 Expr(Box<Expr>),
1001}
1002
1003impl SeedsExpr {
1004 fn list_mut(&mut self) -> Option<&mut Punctuated<Expr, Token![,]>> {
1006 match self {
1007 SeedsExpr::List(list) => Some(list),
1008 SeedsExpr::Expr(_) => None,
1009 }
1010 }
1011
1012 pub fn pop(&mut self) -> Option<syn::punctuated::Pair<Expr, Token![,]>> {
1017 self.list_mut()?.pop()
1018 }
1019
1020 pub fn push_value(&mut self, value: Expr) {
1021 if let Some(list) = self.list_mut() {
1022 list.push_value(value);
1023 }
1024 }
1025
1026 pub fn is_empty(&self) -> bool {
1030 match self {
1031 SeedsExpr::List(list) => list.is_empty(),
1032 SeedsExpr::Expr(_) => false, }
1034 }
1035
1036 pub fn iter(&self) -> Box<dyn Iterator<Item = &Expr> + '_> {
1038 match self {
1039 SeedsExpr::List(list) => Box::new(list.iter()),
1040 SeedsExpr::Expr(expr) => Box::new(std::iter::once(expr.as_ref())),
1041 }
1042 }
1043
1044 pub fn len(&self) -> usize {
1046 match self {
1047 SeedsExpr::List(list) => list.len(),
1048 SeedsExpr::Expr(_) => 1,
1049 }
1050 }
1051}
1052
1053impl quote::ToTokens for SeedsExpr {
1055 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
1056 match self {
1057 SeedsExpr::List(list) => list.to_tokens(tokens),
1058 SeedsExpr::Expr(expr) => expr.to_tokens(tokens),
1059 }
1060 }
1061}
1062
1063impl syn::parse::Parse for SeedsExpr {
1064 fn parse(stream: syn::parse::ParseStream) -> syn::parse::Result<Self> {
1065 if stream.peek(syn::token::Bracket) {
1066 let content;
1067 syn::bracketed!(content in stream);
1068 let mut list: Punctuated<Expr, Token![,]> =
1069 content.parse_terminated(Expr::parse, Token![,])?;
1070 list.pop_punct();
1071
1072 Ok(SeedsExpr::List(list))
1073 } else {
1074 Ok(SeedsExpr::Expr(Box::new(stream.parse()?)))
1075 }
1076 }
1077}
1078
1079#[derive(Debug, Clone)]
1080pub struct ConstraintSeedsGroup {
1081 pub is_init: bool,
1082 pub seeds: SeedsExpr,
1083 pub bump: Option<Expr>, pub program_seed: Option<Expr>, }
1086
1087#[derive(Debug, Clone)]
1088pub struct ConstraintSeeds {
1089 pub seeds: SeedsExpr,
1090}
1091
1092#[derive(Debug, Clone)]
1093pub struct ConstraintExecutable {}
1094
1095#[derive(Debug, Clone)]
1096pub struct ConstraintPayer {
1097 pub target: Expr,
1098}
1099
1100#[derive(Debug, Clone)]
1101pub struct ConstraintSpace {
1102 pub space: Expr,
1103}
1104
1105#[derive(Debug, Clone)]
1107pub struct ConstraintExtensionAuthority {
1108 pub authority: Expr,
1109}
1110
1111#[derive(Debug, Clone)]
1112pub struct ConstraintExtensionGroupPointerGroupAddress {
1113 pub group_address: Expr,
1114}
1115
1116#[derive(Debug, Clone)]
1117pub struct ConstraintExtensionGroupMemberPointerMemberAddress {
1118 pub member_address: Expr,
1119}
1120
1121#[derive(Debug, Clone)]
1122pub struct ConstraintExtensionMetadataPointerMetadataAddress {
1123 pub metadata_address: Expr,
1124}
1125
1126#[derive(Debug, Clone)]
1127pub struct ConstraintExtensionTokenHookProgramId {
1128 pub program_id: Expr,
1129}
1130
1131#[derive(Debug, Clone)]
1132pub struct ConstraintExtensionPermanentDelegate {
1133 pub permanent_delegate: Expr,
1134}
1135
1136#[derive(Debug, Clone)]
1137#[allow(clippy::large_enum_variant)]
1138pub enum InitKind {
1139 Program {
1140 owner: Option<Expr>,
1141 },
1142 Interface {
1143 owner: Option<Expr>,
1144 },
1145 Token {
1148 owner: Expr,
1149 mint: Expr,
1150 token_program: Option<Expr>,
1151 },
1152 AssociatedToken {
1153 owner: Expr,
1154 mint: Expr,
1155 token_program: Option<Expr>,
1156 },
1157 Mint {
1158 owner: Expr,
1159 freeze_authority: Option<Expr>,
1160 decimals: Expr,
1161 token_program: Option<Expr>,
1162 group_pointer_authority: Option<Expr>,
1164 group_pointer_group_address: Option<Expr>,
1165 group_member_pointer_authority: Option<Expr>,
1166 group_member_pointer_member_address: Option<Expr>,
1167 metadata_pointer_authority: Option<Expr>,
1168 metadata_pointer_metadata_address: Option<Expr>,
1169 close_authority: Option<Expr>,
1170 permanent_delegate: Option<Expr>,
1171 transfer_hook_authority: Option<Expr>,
1172 transfer_hook_program_id: Option<Expr>,
1173 pausable_authority: Option<Expr>,
1174 },
1175}
1176
1177#[derive(Debug, Clone)]
1178pub struct ConstraintClose {
1179 pub sol_dest: Ident,
1180}
1181
1182#[derive(Debug, Clone)]
1183pub struct ConstraintTokenMint {
1184 pub mint: Expr,
1185}
1186
1187#[derive(Debug, Clone)]
1188pub struct ConstraintMintConfidentialTransferData {
1189 pub confidential_transfer_data: Expr,
1190}
1191
1192#[derive(Debug, Clone)]
1193pub struct ConstraintMintMetadata {
1194 pub token_metadata: Expr,
1195}
1196
1197#[derive(Debug, Clone)]
1198pub struct ConstraintMintTokenGroupData {
1199 pub token_group_data: Expr,
1200}
1201
1202#[derive(Debug, Clone)]
1203pub struct ConstraintMintTokenGroupMemberData {
1204 pub token_group_member_data: Expr,
1205}
1206
1207#[derive(Debug, Clone)]
1208pub struct ConstraintMintMetadataPointerData {
1209 pub metadata_pointer_data: Expr,
1210}
1211
1212#[derive(Debug, Clone)]
1213pub struct ConstraintMintGroupPointerData {
1214 pub group_pointer_data: Expr,
1215}
1216
1217#[derive(Debug, Clone)]
1218pub struct ConstraintMintGroupMemberPointerData {
1219 pub group_member_pointer_data: Expr,
1220}
1221
1222#[derive(Debug, Clone)]
1223pub struct ConstraintMintCloseAuthority {
1224 pub close_authority: Expr,
1225}
1226
1227#[derive(Debug, Clone)]
1228pub struct ConstraintTokenAuthority {
1229 pub auth: Expr,
1230}
1231
1232#[derive(Debug, Clone)]
1233pub struct ConstraintTokenProgram {
1234 token_program: Expr,
1235}
1236
1237#[derive(Debug, Clone)]
1238pub struct ConstraintMintAuthority {
1239 pub mint_auth: Expr,
1240}
1241
1242#[derive(Debug, Clone)]
1243pub struct ConstraintMintFreezeAuthority {
1244 pub mint_freeze_auth: Expr,
1245}
1246
1247#[derive(Debug, Clone)]
1248pub struct ConstraintMintDecimals {
1249 pub decimals: Expr,
1250}
1251
1252#[derive(Debug, Clone)]
1253pub struct ConstraintTokenBump {
1254 pub bump: Option<Expr>,
1255}
1256
1257#[derive(Debug, Clone)]
1258pub struct ConstraintProgramSeed {
1259 pub program_seed: Expr,
1260}
1261
1262#[derive(Debug, Clone)]
1263pub struct ConstraintAssociatedToken {
1264 pub wallet: Expr,
1265 pub mint: Expr,
1266 pub token_program: Option<Expr>,
1267}
1268
1269#[derive(Debug, Clone)]
1270pub struct ConstraintTokenAccountGroup {
1271 pub mint: Option<Expr>,
1272 pub authority: Option<Expr>,
1273 pub token_program: Option<Expr>,
1274}
1275
1276#[derive(Debug, Clone)]
1277pub struct ConstraintTokenMintGroup {
1278 pub decimals: Option<Expr>,
1279 pub mint_authority: Option<Expr>,
1280 pub freeze_authority: Option<Expr>,
1281 pub token_program: Option<Expr>,
1282 pub group_pointer_authority: Option<Expr>,
1283 pub group_pointer_group_address: Option<Expr>,
1284 pub group_member_pointer_authority: Option<Expr>,
1285 pub group_member_pointer_member_address: Option<Expr>,
1286 pub metadata_pointer_authority: Option<Expr>,
1287 pub metadata_pointer_metadata_address: Option<Expr>,
1288 pub close_authority: Option<Expr>,
1289 pub permanent_delegate: Option<Expr>,
1290 pub transfer_hook_authority: Option<Expr>,
1291 pub transfer_hook_program_id: Option<Expr>,
1292 pub pausable_authority: Option<Expr>,
1293}
1294
1295#[derive(Debug, Clone)]
1297pub struct Context<T> {
1298 span: Span,
1299 inner: T,
1300}
1301
1302impl<T> Context<T> {
1303 pub fn new(span: Span, inner: T) -> Self {
1304 Self { span, inner }
1305 }
1306
1307 pub fn into_inner(self) -> T {
1308 self.inner
1309 }
1310}
1311
1312impl<T> Deref for Context<T> {
1313 type Target = T;
1314
1315 fn deref(&self) -> &Self::Target {
1316 &self.inner
1317 }
1318}
1319
1320impl<T> Context<T> {
1321 pub fn span(&self) -> Span {
1322 self.span
1323 }
1324}
1325
1326impl<T: ToTokens> ToTokens for Context<T> {
1327 fn to_tokens(&self, tokens: &mut TokenStream) {
1328 self.inner.to_tokens(tokens)
1329 }
1330}