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 owner_addr = match &kind {
448 None => quote! { __program_id },
449 Some(InitKind::Program { .. }) => quote! {
450 __program_id
451 },
452 _ => quote! {
453 &anchor_spl::token::ID
454 },
455 };
456 match &self.ty {
457 Ty::AccountInfo => quote! { #field.to_account_info() },
458 Ty::UncheckedAccount => {
459 quote! { UncheckedAccount::try_from(&#field) }
460 }
461 Ty::Account(AccountTy { boxed, .. })
462 | Ty::InterfaceAccount(InterfaceAccountTy { boxed, .. }) => {
463 let stream = if checked {
464 quote! {
465 match #container_ty::try_from(&#field) {
466 Ok(val) => val,
467 Err(e) => return Err(e.with_account_name(#field_str))
468 }
469 }
470 } else {
471 quote! {
472 match #container_ty::try_from_unchecked(&#field) {
473 Ok(val) => val,
474 Err(e) => return Err(e.with_account_name(#field_str))
475 }
476 }
477 };
478 if *boxed {
479 quote! {
480 Box::new(#stream)
481 }
482 } else {
483 stream
484 }
485 }
486 Ty::LazyAccount(_) => {
487 if checked {
488 quote! {
489 match #container_ty::try_from(&#field) {
490 Ok(val) => val,
491 Err(e) => return Err(e.with_account_name(#field_str))
492 }
493 }
494 } else {
495 quote! {
496 match #container_ty::try_from_unchecked(&#field) {
497 Ok(val) => val,
498 Err(e) => return Err(e.with_account_name(#field_str))
499 }
500 }
501 }
502 }
503 Ty::AccountLoader(_) => {
504 if checked {
505 quote! {
506 match #container_ty::try_from(&#field) {
507 Ok(val) => val,
508 Err(e) => return Err(e.with_account_name(#field_str))
509 }
510 }
511 } else {
512 quote! {
513 match #container_ty::try_from_unchecked(#owner_addr, &#field) {
514 Ok(val) => val,
515 Err(e) => return Err(e.with_account_name(#field_str))
516 }
517 }
518 }
519 }
520 _ => {
521 if checked {
522 quote! {
523 match #container_ty::try_from(#owner_addr, &#field) {
524 Ok(val) => val,
525 Err(e) => return Err(e.with_account_name(#field_str))
526 }
527 }
528 } else {
529 quote! {
530 match #container_ty::try_from_unchecked(#owner_addr, &#field) {
531 Ok(val) => val,
532 Err(e) => return Err(e.with_account_name(#field_str))
533 }
534 }
535 }
536 }
537 }
538 }
539
540 pub fn container_ty(&self) -> proc_macro2::TokenStream {
541 match &self.ty {
542 Ty::Account(_) => quote! {
543 anchor_lang::accounts::account::Account
544 },
545 Ty::LazyAccount(_) => quote! {
546 anchor_lang::accounts::lazy_account::LazyAccount
547 },
548 Ty::AccountLoader(_) => quote! {
549 anchor_lang::accounts::account_loader::AccountLoader
550 },
551 Ty::Migration(_) => quote! {
552 anchor_lang::accounts::migration::Migration
553 },
554 Ty::Sysvar(_) => quote! { anchor_lang::accounts::sysvar::Sysvar },
555 Ty::Program(_) => quote! { anchor_lang::accounts::program::Program },
556 Ty::Interface(_) => quote! { anchor_lang::accounts::interface::Interface },
557 Ty::InterfaceAccount(_) => {
558 quote! { anchor_lang::accounts::interface_account::InterfaceAccount }
559 }
560 Ty::AccountInfo => quote! {},
561 Ty::UncheckedAccount => quote! {},
562 Ty::Signer => quote! {},
563 Ty::SystemAccount => quote! {},
564 Ty::ProgramData => quote! {},
565 }
566 }
567
568 pub fn account_ty(&self) -> proc_macro2::TokenStream {
570 match &self.ty {
571 Ty::AccountInfo => quote! {
572 AccountInfo
573 },
574 Ty::UncheckedAccount => quote! {
575 UncheckedAccount
576 },
577 Ty::Signer => quote! {
578 Signer
579 },
580 Ty::SystemAccount => quote! {
581 SystemAccount
582 },
583 Ty::ProgramData => quote! {
584 ProgramData
585 },
586 Ty::Account(ty) => {
587 let ident = &ty.account_type_path;
588 quote! {
589 #ident
590 }
591 }
592 Ty::LazyAccount(ty) => {
593 let ident = &ty.account_type_path;
594 quote! {
595 #ident
596 }
597 }
598 Ty::InterfaceAccount(ty) => {
599 let ident = &ty.account_type_path;
600 quote! {
601 #ident
602 }
603 }
604 Ty::AccountLoader(ty) => {
605 let ident = &ty.account_type_path;
606 quote! {
607 #ident
608 }
609 }
610 Ty::Migration(ty) => {
611 let from = &ty.from_type_path;
613 quote! {
614 #from
615 }
616 }
617 Ty::Sysvar(ty) => match ty {
618 SysvarTy::Clock => quote! {Clock},
619 SysvarTy::Rent => quote! {Rent},
620 SysvarTy::EpochSchedule => quote! {EpochSchedule},
621 SysvarTy::Fees => quote! {Fees},
622 SysvarTy::RecentBlockhashes => quote! {RecentBlockhashes},
623 SysvarTy::SlotHashes => quote! {SlotHashes},
624 SysvarTy::SlotHistory => quote! {SlotHistory},
625 SysvarTy::StakeHistory => quote! {StakeHistory},
626 SysvarTy::Instructions => quote! {Instructions},
627 SysvarTy::Rewards => quote! {Rewards},
628 },
629 Ty::Program(ty) => {
630 let program = &ty.account_type_path;
631 let program_str = quote!(#program).to_string();
633 if program_str == "__SolanaProgramUnitType" {
634 quote! {}
635 } else {
636 quote! {
637 #program
638 }
639 }
640 }
641 Ty::Interface(ty) => {
642 let program = &ty.account_type_path;
643 quote! {
644 #program
645 }
646 }
647 }
648 }
649}
650
651#[derive(Debug)]
652pub struct CompositeField {
653 pub ident: Ident,
654 pub constraints: ConstraintGroup,
655 pub symbol: String,
656 pub raw_field: syn::Field,
657 pub docs: Option<Vec<String>>,
659}
660
661#[derive(Debug, PartialEq, Eq)]
663pub enum Ty {
664 AccountInfo,
665 UncheckedAccount,
666 AccountLoader(AccountLoaderTy),
667 Sysvar(SysvarTy),
668 Account(AccountTy),
669 LazyAccount(LazyAccountTy),
670 Migration(MigrationTy),
671 Program(ProgramTy),
672 Interface(InterfaceTy),
673 InterfaceAccount(InterfaceAccountTy),
674 Signer,
675 SystemAccount,
676 ProgramData,
677}
678
679#[derive(Debug, PartialEq, Eq)]
680pub enum SysvarTy {
681 Clock,
682 Rent,
683 EpochSchedule,
684 Fees,
685 RecentBlockhashes,
686 SlotHashes,
687 SlotHistory,
688 StakeHistory,
689 Instructions,
690 Rewards,
691}
692
693#[derive(Debug, PartialEq, Eq)]
694pub struct AccountLoaderTy {
695 pub account_type_path: TypePath,
697}
698
699#[derive(Debug, PartialEq, Eq)]
700pub struct AccountTy {
701 pub account_type_path: TypePath,
703 pub boxed: bool,
705}
706
707#[derive(Debug, PartialEq, Eq)]
708pub struct LazyAccountTy {
709 pub account_type_path: TypePath,
711}
712
713#[derive(Debug, PartialEq, Eq)]
714pub struct MigrationTy {
715 pub from_type_path: TypePath,
717 pub to_type_path: TypePath,
718}
719
720#[derive(Debug, PartialEq, Eq)]
721pub struct InterfaceAccountTy {
722 pub account_type_path: TypePath,
724 pub boxed: bool,
726}
727
728#[derive(Debug, PartialEq, Eq)]
729pub struct ProgramTy {
730 pub account_type_path: TypePath,
732}
733
734#[derive(Debug, PartialEq, Eq)]
735pub struct InterfaceTy {
736 pub account_type_path: TypePath,
738}
739
740#[derive(Debug)]
741pub struct Error {
742 pub name: String,
743 pub raw_enum: ItemEnum,
744 pub ident: Ident,
745 pub codes: Vec<ErrorCode>,
746 pub args: Option<ErrorArgs>,
747}
748
749#[derive(Debug)]
750pub struct ErrorArgs {
751 pub offset: LitInt,
752}
753
754impl Parse for ErrorArgs {
755 fn parse(stream: ParseStream) -> ParseResult<Self> {
756 let offset_span = stream.span();
757 let offset = stream.call(Ident::parse_any)?;
758 if offset.to_string().as_str() != "offset" {
759 return Err(ParseError::new(offset_span, "expected keyword offset"));
760 }
761 stream.parse::<Token![=]>()?;
762 let offset: LitInt = stream.parse()?;
763 Ok(ErrorArgs { offset })
764 }
765}
766
767#[derive(Debug)]
768pub struct ErrorCode {
769 pub id: u32,
770 pub ident: Ident,
771 pub msg: Option<String>,
772}
773
774#[derive(Debug, Default, Clone)]
776pub struct ConstraintGroup {
777 pub init: Option<ConstraintInitGroup>,
778 pub zeroed: Option<ConstraintZeroed>,
779 pub mutable: Option<ConstraintMut>,
780 pub dup: Option<ConstraintDup>,
781 pub signer: Option<ConstraintSigner>,
782 pub owner: Option<ConstraintOwner>,
783 pub rent_exempt: Option<ConstraintRentExempt>,
784 pub seeds: Option<ConstraintSeedsGroup>,
785 pub executable: Option<ConstraintExecutable>,
786 pub has_one: Vec<ConstraintHasOne>,
787 pub raw: Vec<ConstraintRaw>,
788 pub close: Option<ConstraintClose>,
789 pub address: Option<ConstraintAddress>,
790 pub associated_token: Option<ConstraintAssociatedToken>,
791 pub token_account: Option<ConstraintTokenAccountGroup>,
792 pub mint: Option<ConstraintTokenMintGroup>,
793 pub realloc: Option<ConstraintReallocGroup>,
794}
795
796impl ConstraintGroup {
797 pub fn is_zeroed(&self) -> bool {
798 self.zeroed.is_some()
799 }
800
801 pub fn is_mutable(&self) -> bool {
802 self.mutable.is_some()
803 }
804
805 pub fn is_dup(&self) -> bool {
806 self.dup.is_some()
807 }
808
809 pub fn is_pure_init(&self) -> bool {
812 matches!(&self.init, Some(c) if !c.if_needed)
813 }
814
815 pub fn is_signer(&self) -> bool {
816 self.signer.is_some()
817 }
818
819 pub fn is_close(&self) -> bool {
820 self.close.is_some()
821 }
822}
823
824#[allow(clippy::large_enum_variant)]
828#[derive(Debug)]
829pub enum Constraint {
830 Init(ConstraintInitGroup),
831 Zeroed(ConstraintZeroed),
832 Mut(ConstraintMut),
833 Dup(ConstraintDup),
834 Signer(ConstraintSigner),
835 HasOne(ConstraintHasOne),
836 Raw(ConstraintRaw),
837 Owner(ConstraintOwner),
838 RentExempt(ConstraintRentExempt),
839 Seeds(ConstraintSeedsGroup),
840 AssociatedToken(ConstraintAssociatedToken),
841 Executable(ConstraintExecutable),
842 Close(ConstraintClose),
843 Address(ConstraintAddress),
844 TokenAccount(ConstraintTokenAccountGroup),
845 Mint(ConstraintTokenMintGroup),
846 Realloc(ConstraintReallocGroup),
847}
848
849#[allow(clippy::large_enum_variant)]
851#[derive(Debug)]
852pub enum ConstraintToken {
853 Init(Context<ConstraintInit>),
854 Zeroed(Context<ConstraintZeroed>),
855 Mut(Context<ConstraintMut>),
856 Dup(Context<ConstraintDup>),
857 Signer(Context<ConstraintSigner>),
858 HasOne(Context<ConstraintHasOne>),
859 Raw(Context<ConstraintRaw>),
860 Owner(Context<ConstraintOwner>),
861 RentExempt(Context<ConstraintRentExempt>),
862 Seeds(Context<ConstraintSeeds>),
863 Executable(Context<ConstraintExecutable>),
864 Close(Context<ConstraintClose>),
865 Payer(Context<ConstraintPayer>),
866 Space(Context<ConstraintSpace>),
867 Address(Context<ConstraintAddress>),
868 TokenMint(Context<ConstraintTokenMint>),
869 TokenAuthority(Context<ConstraintTokenAuthority>),
870 TokenTokenProgram(Context<ConstraintTokenProgram>),
871 AssociatedTokenMint(Context<ConstraintTokenMint>),
872 AssociatedTokenAuthority(Context<ConstraintTokenAuthority>),
873 AssociatedTokenTokenProgram(Context<ConstraintTokenProgram>),
874 MintAuthority(Context<ConstraintMintAuthority>),
875 MintFreezeAuthority(Context<ConstraintMintFreezeAuthority>),
876 MintDecimals(Context<ConstraintMintDecimals>),
877 MintTokenProgram(Context<ConstraintTokenProgram>),
878 Bump(Context<ConstraintTokenBump>),
879 ProgramSeed(Context<ConstraintProgramSeed>),
880 Realloc(Context<ConstraintRealloc>),
881 ReallocPayer(Context<ConstraintReallocPayer>),
882 ReallocZero(Context<ConstraintReallocZero>),
883 ExtensionGroupPointerAuthority(Context<ConstraintExtensionAuthority>),
885 ExtensionGroupPointerGroupAddress(Context<ConstraintExtensionGroupPointerGroupAddress>),
886 ExtensionGroupMemberPointerAuthority(Context<ConstraintExtensionAuthority>),
887 ExtensionGroupMemberPointerMemberAddress(
888 Context<ConstraintExtensionGroupMemberPointerMemberAddress>,
889 ),
890 ExtensionMetadataPointerAuthority(Context<ConstraintExtensionAuthority>),
891 ExtensionMetadataPointerMetadataAddress(
892 Context<ConstraintExtensionMetadataPointerMetadataAddress>,
893 ),
894 ExtensionCloseAuthority(Context<ConstraintExtensionAuthority>),
895 ExtensionTokenHookAuthority(Context<ConstraintExtensionAuthority>),
896 ExtensionTokenHookProgramId(Context<ConstraintExtensionTokenHookProgramId>),
897 ExtensionPermanentDelegate(Context<ConstraintExtensionPermanentDelegate>),
898 ExtensionPausableAuthority(Context<ConstraintExtensionAuthority>),
899}
900
901impl Parse for ConstraintToken {
902 fn parse(stream: ParseStream) -> ParseResult<Self> {
903 accounts_parser::constraints::parse_token(stream)
904 }
905}
906
907#[derive(Debug, Clone)]
908pub struct ConstraintInit {
909 pub if_needed: bool,
910}
911
912#[derive(Debug, Clone)]
913pub struct ConstraintInitIfNeeded {}
914
915#[derive(Debug, Clone)]
916pub struct ConstraintZeroed {}
917
918#[derive(Debug, Clone)]
919pub struct ConstraintMut {
920 pub error: Option<Expr>,
921}
922
923#[derive(Debug, Clone)]
924pub struct ConstraintDup {}
925
926#[derive(Debug, Clone)]
927pub struct ConstraintReallocGroup {
928 pub payer: Expr,
929 pub space: Expr,
930 pub zero: Expr,
931}
932
933#[derive(Debug, Clone)]
934pub struct ConstraintRealloc {
935 pub space: Expr,
936}
937
938#[derive(Debug, Clone)]
939pub struct ConstraintReallocPayer {
940 pub target: Expr,
941}
942
943#[derive(Debug, Clone)]
944pub struct ConstraintReallocZero {
945 pub zero: Expr,
946}
947
948#[derive(Debug, Clone)]
949pub struct ConstraintSigner {
950 pub error: Option<Expr>,
951}
952
953#[derive(Debug, Clone)]
954pub struct ConstraintHasOne {
955 pub join_target: Expr,
956 pub error: Option<Expr>,
957}
958
959#[derive(Debug, Clone)]
960pub struct ConstraintRaw {
961 pub raw: Expr,
962 pub error: Option<Expr>,
963}
964
965#[derive(Debug, Clone)]
966pub struct ConstraintOwner {
967 pub owner_address: Expr,
968 pub error: Option<Expr>,
969}
970
971#[derive(Debug, Clone)]
972pub struct ConstraintAddress {
973 pub address: Expr,
974 pub error: Option<Expr>,
975}
976
977#[derive(Debug, Clone)]
978pub enum ConstraintRentExempt {
979 Enforce,
980 Skip,
981}
982
983#[derive(Debug, Clone)]
984pub struct ConstraintInitGroup {
985 pub if_needed: bool,
986 pub seeds: Option<ConstraintSeedsGroup>,
987 pub payer: Expr,
988 pub space: Option<Expr>,
989 pub kind: InitKind,
990}
991
992#[derive(Debug, Clone)]
995pub enum SeedsExpr {
996 List(Punctuated<Expr, Token![,]>),
998 Expr(Box<Expr>),
1000}
1001
1002impl SeedsExpr {
1003 fn list_mut(&mut self) -> Option<&mut Punctuated<Expr, Token![,]>> {
1005 match self {
1006 SeedsExpr::List(list) => Some(list),
1007 SeedsExpr::Expr(_) => None,
1008 }
1009 }
1010
1011 pub fn pop(&mut self) -> Option<syn::punctuated::Pair<Expr, Token![,]>> {
1016 self.list_mut()?.pop()
1017 }
1018
1019 pub fn push_value(&mut self, value: Expr) {
1020 if let Some(list) = self.list_mut() {
1021 list.push_value(value);
1022 }
1023 }
1024
1025 pub fn is_empty(&self) -> bool {
1029 match self {
1030 SeedsExpr::List(list) => list.is_empty(),
1031 SeedsExpr::Expr(_) => false, }
1033 }
1034
1035 pub fn iter(&self) -> Box<dyn Iterator<Item = &Expr> + '_> {
1037 match self {
1038 SeedsExpr::List(list) => Box::new(list.iter()),
1039 SeedsExpr::Expr(expr) => Box::new(std::iter::once(expr.as_ref())),
1040 }
1041 }
1042
1043 pub fn len(&self) -> usize {
1045 match self {
1046 SeedsExpr::List(list) => list.len(),
1047 SeedsExpr::Expr(_) => 1,
1048 }
1049 }
1050}
1051
1052impl quote::ToTokens for SeedsExpr {
1054 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
1055 match self {
1056 SeedsExpr::List(list) => list.to_tokens(tokens),
1057 SeedsExpr::Expr(expr) => expr.to_tokens(tokens),
1058 }
1059 }
1060}
1061
1062impl syn::parse::Parse for SeedsExpr {
1063 fn parse(stream: syn::parse::ParseStream) -> syn::parse::Result<Self> {
1064 if stream.peek(syn::token::Bracket) {
1065 let content;
1066 syn::bracketed!(content in stream);
1067 let mut list: Punctuated<Expr, Token![,]> =
1068 content.parse_terminated(Expr::parse, Token![,])?;
1069 list.pop_punct();
1070
1071 Ok(SeedsExpr::List(list))
1072 } else {
1073 Ok(SeedsExpr::Expr(Box::new(stream.parse()?)))
1074 }
1075 }
1076}
1077
1078#[derive(Debug, Clone)]
1079pub struct ConstraintSeedsGroup {
1080 pub is_init: bool,
1081 pub seeds: SeedsExpr,
1082 pub bump: Option<Expr>, pub program_seed: Option<Expr>, }
1085
1086#[derive(Debug, Clone)]
1087pub struct ConstraintSeeds {
1088 pub seeds: SeedsExpr,
1089}
1090
1091#[derive(Debug, Clone)]
1092pub struct ConstraintExecutable {}
1093
1094#[derive(Debug, Clone)]
1095pub struct ConstraintPayer {
1096 pub target: Expr,
1097}
1098
1099#[derive(Debug, Clone)]
1100pub struct ConstraintSpace {
1101 pub space: Expr,
1102}
1103
1104#[derive(Debug, Clone)]
1106pub struct ConstraintExtensionAuthority {
1107 pub authority: Expr,
1108}
1109
1110#[derive(Debug, Clone)]
1111pub struct ConstraintExtensionGroupPointerGroupAddress {
1112 pub group_address: Expr,
1113}
1114
1115#[derive(Debug, Clone)]
1116pub struct ConstraintExtensionGroupMemberPointerMemberAddress {
1117 pub member_address: Expr,
1118}
1119
1120#[derive(Debug, Clone)]
1121pub struct ConstraintExtensionMetadataPointerMetadataAddress {
1122 pub metadata_address: Expr,
1123}
1124
1125#[derive(Debug, Clone)]
1126pub struct ConstraintExtensionTokenHookProgramId {
1127 pub program_id: Expr,
1128}
1129
1130#[derive(Debug, Clone)]
1131pub struct ConstraintExtensionPermanentDelegate {
1132 pub permanent_delegate: Expr,
1133}
1134
1135#[derive(Debug, Clone)]
1136#[allow(clippy::large_enum_variant)]
1137pub enum InitKind {
1138 Program {
1139 owner: Option<Expr>,
1140 },
1141 Interface {
1142 owner: Option<Expr>,
1143 },
1144 Token {
1147 owner: Expr,
1148 mint: Expr,
1149 token_program: Option<Expr>,
1150 },
1151 AssociatedToken {
1152 owner: Expr,
1153 mint: Expr,
1154 token_program: Option<Expr>,
1155 },
1156 Mint {
1157 owner: Expr,
1158 freeze_authority: Option<Expr>,
1159 decimals: Expr,
1160 token_program: Option<Expr>,
1161 group_pointer_authority: Option<Expr>,
1163 group_pointer_group_address: Option<Expr>,
1164 group_member_pointer_authority: Option<Expr>,
1165 group_member_pointer_member_address: Option<Expr>,
1166 metadata_pointer_authority: Option<Expr>,
1167 metadata_pointer_metadata_address: Option<Expr>,
1168 close_authority: Option<Expr>,
1169 permanent_delegate: Option<Expr>,
1170 transfer_hook_authority: Option<Expr>,
1171 transfer_hook_program_id: Option<Expr>,
1172 pausable_authority: Option<Expr>,
1173 },
1174}
1175
1176#[derive(Debug, Clone)]
1177pub struct ConstraintClose {
1178 pub sol_dest: Ident,
1179}
1180
1181#[derive(Debug, Clone)]
1182pub struct ConstraintTokenMint {
1183 pub mint: Expr,
1184}
1185
1186#[derive(Debug, Clone)]
1187pub struct ConstraintMintConfidentialTransferData {
1188 pub confidential_transfer_data: Expr,
1189}
1190
1191#[derive(Debug, Clone)]
1192pub struct ConstraintMintMetadata {
1193 pub token_metadata: Expr,
1194}
1195
1196#[derive(Debug, Clone)]
1197pub struct ConstraintMintTokenGroupData {
1198 pub token_group_data: Expr,
1199}
1200
1201#[derive(Debug, Clone)]
1202pub struct ConstraintMintTokenGroupMemberData {
1203 pub token_group_member_data: Expr,
1204}
1205
1206#[derive(Debug, Clone)]
1207pub struct ConstraintMintMetadataPointerData {
1208 pub metadata_pointer_data: Expr,
1209}
1210
1211#[derive(Debug, Clone)]
1212pub struct ConstraintMintGroupPointerData {
1213 pub group_pointer_data: Expr,
1214}
1215
1216#[derive(Debug, Clone)]
1217pub struct ConstraintMintGroupMemberPointerData {
1218 pub group_member_pointer_data: Expr,
1219}
1220
1221#[derive(Debug, Clone)]
1222pub struct ConstraintMintCloseAuthority {
1223 pub close_authority: Expr,
1224}
1225
1226#[derive(Debug, Clone)]
1227pub struct ConstraintTokenAuthority {
1228 pub auth: Expr,
1229}
1230
1231#[derive(Debug, Clone)]
1232pub struct ConstraintTokenProgram {
1233 token_program: Expr,
1234}
1235
1236#[derive(Debug, Clone)]
1237pub struct ConstraintMintAuthority {
1238 pub mint_auth: Expr,
1239}
1240
1241#[derive(Debug, Clone)]
1242pub struct ConstraintMintFreezeAuthority {
1243 pub mint_freeze_auth: Expr,
1244}
1245
1246#[derive(Debug, Clone)]
1247pub struct ConstraintMintDecimals {
1248 pub decimals: Expr,
1249}
1250
1251#[derive(Debug, Clone)]
1252pub struct ConstraintTokenBump {
1253 pub bump: Option<Expr>,
1254}
1255
1256#[derive(Debug, Clone)]
1257pub struct ConstraintProgramSeed {
1258 pub program_seed: Expr,
1259}
1260
1261#[derive(Debug, Clone)]
1262pub struct ConstraintAssociatedToken {
1263 pub wallet: Expr,
1264 pub mint: Expr,
1265 pub token_program: Option<Expr>,
1266}
1267
1268#[derive(Debug, Clone)]
1269pub struct ConstraintTokenAccountGroup {
1270 pub mint: Option<Expr>,
1271 pub authority: Option<Expr>,
1272 pub token_program: Option<Expr>,
1273}
1274
1275#[derive(Debug, Clone)]
1276pub struct ConstraintTokenMintGroup {
1277 pub decimals: Option<Expr>,
1278 pub mint_authority: Option<Expr>,
1279 pub freeze_authority: Option<Expr>,
1280 pub token_program: Option<Expr>,
1281 pub group_pointer_authority: Option<Expr>,
1282 pub group_pointer_group_address: Option<Expr>,
1283 pub group_member_pointer_authority: Option<Expr>,
1284 pub group_member_pointer_member_address: Option<Expr>,
1285 pub metadata_pointer_authority: Option<Expr>,
1286 pub metadata_pointer_metadata_address: Option<Expr>,
1287 pub close_authority: Option<Expr>,
1288 pub permanent_delegate: Option<Expr>,
1289 pub transfer_hook_authority: Option<Expr>,
1290 pub transfer_hook_program_id: Option<Expr>,
1291 pub pausable_authority: Option<Expr>,
1292}
1293
1294#[derive(Debug, Clone)]
1296pub struct Context<T> {
1297 span: Span,
1298 inner: T,
1299}
1300
1301impl<T> Context<T> {
1302 pub fn new(span: Span, inner: T) -> Self {
1303 Self { span, inner }
1304 }
1305
1306 pub fn into_inner(self) -> T {
1307 self.inner
1308 }
1309}
1310
1311impl<T> Deref for Context<T> {
1312 type Target = T;
1313
1314 fn deref(&self) -> &Self::Target {
1315 &self.inner
1316 }
1317}
1318
1319impl<T> Context<T> {
1320 pub fn span(&self) -> Span {
1321 self.span
1322 }
1323}
1324
1325impl<T: ToTokens> ToTokens for Context<T> {
1326 fn to_tokens(&self, tokens: &mut TokenStream) {
1327 self.inner.to_tokens(tokens)
1328 }
1329}