Skip to main content

anchor_syn/parser/program/
mod.rs

1use {
2    crate::{parser::docs, Program},
3    syn::{
4        parse::{Error as ParseError, Result as ParseResult},
5        spanned::Spanned,
6    },
7};
8
9mod instructions;
10
11pub fn parse(program_mod: syn::ItemMod) -> ParseResult<Program> {
12    let docs = docs::parse(&program_mod.attrs);
13    let (ixs, fallback_fn) = instructions::parse(&program_mod)?;
14    Ok(Program {
15        ixs,
16        name: program_mod.ident.clone(),
17        docs,
18        program_mod,
19        fallback_fn,
20        program_args: None,
21    })
22}
23
24/// Whether a function in a program is an ix handler, a fallback fn or unrecognized
25enum FunctionType {
26    /// Regular instruction handler - takes a `Context<Account>` and other arguments
27    IxHandler,
28    /// Fallback method - takes `(&Pubkey, &[AccountInfo], &[u8])`
29    Fallback,
30    /// Invalid method type, raises an error
31    Error(ParseError),
32}
33
34/// Identify a function type via the parameters
35fn function_type(method: &syn::ItemFn) -> FunctionType {
36    let inputs = method
37        .sig
38        .inputs
39        .iter()
40        .map(|arg| {
41            let syn::FnArg::Typed(arg) = arg else {
42                return Err(ParseError::new(
43                    arg.span(),
44                    "handlers may not take receivers",
45                ));
46            };
47            Ok(arg)
48        })
49        .collect::<ParseResult<Vec<_>>>();
50
51    let inputs = match inputs {
52        Ok(i) => i,
53        Err(e) => {
54            return FunctionType::Error(e);
55        }
56    };
57
58    fn named_args(args: &[&syn::PatType]) -> bool {
59        args.iter()
60            .all(|arg| matches!(&*arg.pat, syn::Pat::Ident(_)))
61    }
62
63    fn valid_handler(context: &syn::Type) -> bool {
64        let syn::Type::Path(context) = context else {
65            return false;
66        };
67        let Some(segment) = context.path.segments.last() else {
68            return false;
69        };
70        matches!(segment,
71            syn::PathSegment {
72                ident,
73                arguments: syn::PathArguments::AngleBracketed(_),
74            } if ident == "Context"
75        )
76    }
77
78    match inputs.as_slice() {
79        [context, ..] if valid_handler(&context.ty) => FunctionType::IxHandler,
80        [_, _, _] if named_args(&inputs) => FunctionType::Fallback,
81        _ => FunctionType::Error(ParseError::new(
82            method.span(),
83            "handlers must take a `Context<...>` argument",
84        )),
85    }
86}
87
88fn ctx_accounts_ident(path_ty: &syn::PatType) -> ParseResult<proc_macro2::Ident> {
89    let p = match &*path_ty.ty {
90        syn::Type::Path(p) => &p.path,
91        _ => return Err(ParseError::new(path_ty.ty.span(), "invalid type")),
92    };
93    let segment = p
94        .segments
95        .first()
96        .ok_or_else(|| ParseError::new(p.segments.span(), "expected generic arguments here"))?;
97
98    let generic_args = match &segment.arguments {
99        syn::PathArguments::AngleBracketed(args) => args,
100        _ => return Err(ParseError::new(path_ty.span(), "missing accounts context")),
101    };
102    let generic_ty = generic_args
103        .args
104        .iter()
105        .filter_map(|arg| match arg {
106            syn::GenericArgument::Type(ty) => Some(ty),
107            _ => None,
108        })
109        .next()
110        .ok_or_else(|| ParseError::new(generic_args.span(), "expected Accounts type"))?;
111
112    let path = match generic_ty {
113        syn::Type::Path(ty_path) => &ty_path.path,
114        _ => {
115            return Err(ParseError::new(
116                generic_ty.span(),
117                "expected Accounts struct type",
118            ));
119        }
120    };
121    Ok(path
122        .segments
123        .first()
124        .ok_or_else(|| ParseError::new(path.span(), "expected a path segment"))?
125        .ident
126        .clone())
127}