Skip to main content

anchor_syn/
lib.rs

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