anchor_syn/parser/program/
mod.rs1use {
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
24enum FunctionType {
26 IxHandler,
28 Fallback,
30 Error(ParseError),
32}
33
34fn 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}