accounts.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. use crate::{
  2. AccountsStruct, Constraint, ConstraintBelongsTo, ConstraintLiteral, ConstraintOwner,
  3. ConstraintSigner, Field, ProgramAccountTy, Ty,
  4. };
  5. pub fn parse(strct: &syn::ItemStruct) -> AccountsStruct {
  6. let fields = match &strct.fields {
  7. syn::Fields::Named(fields) => fields,
  8. _ => panic!("invalid input"),
  9. };
  10. let fields: Vec<Field> = fields
  11. .named
  12. .iter()
  13. .map(|f: &syn::Field| {
  14. let anchor_attr = {
  15. let anchor_attrs: Vec<&syn::Attribute> = f
  16. .attrs
  17. .iter()
  18. .filter_map(|attr: &syn::Attribute| {
  19. if attr.path.segments.len() != 1 {
  20. return None;
  21. }
  22. if attr.path.segments[0].ident.to_string() != "account" {
  23. return None;
  24. }
  25. Some(attr)
  26. })
  27. .collect();
  28. assert!(anchor_attrs.len() == 1);
  29. anchor_attrs[0]
  30. };
  31. parse_field(f, anchor_attr)
  32. })
  33. .collect();
  34. AccountsStruct::new(strct.clone(), fields)
  35. }
  36. // Parses an inert #[anchor] attribute specifying the DSL.
  37. fn parse_field(f: &syn::Field, anchor: &syn::Attribute) -> Field {
  38. let ident = f.ident.clone().unwrap();
  39. let ty = parse_ty(f);
  40. let (constraints, is_mut, is_signer) = parse_constraints(anchor, &ty);
  41. Field {
  42. ident,
  43. ty,
  44. constraints,
  45. is_mut,
  46. is_signer,
  47. }
  48. }
  49. fn parse_ty(f: &syn::Field) -> Ty {
  50. let path = match &f.ty {
  51. syn::Type::Path(ty_path) => ty_path.path.clone(),
  52. _ => panic!("invalid type"),
  53. };
  54. // TODO: allow segmented paths.
  55. assert!(path.segments.len() == 1);
  56. let segments = &path.segments[0];
  57. match segments.ident.to_string().as_str() {
  58. "ProgramAccount" => Ty::ProgramAccount(parse_program_account(&path)),
  59. "AccountInfo" => Ty::AccountInfo,
  60. _ => panic!("invalid type"),
  61. }
  62. }
  63. fn parse_program_account(path: &syn::Path) -> ProgramAccountTy {
  64. let segments = &path.segments[0];
  65. let account_ident = match &segments.arguments {
  66. syn::PathArguments::AngleBracketed(args) => {
  67. // Expected: <'info, MyType>.
  68. assert!(args.args.len() == 2);
  69. match &args.args[1] {
  70. syn::GenericArgument::Type(ty) => match ty {
  71. syn::Type::Path(ty_path) => {
  72. // TODO: allow segmented paths.
  73. assert!(ty_path.path.segments.len() == 1);
  74. let path_segment = &ty_path.path.segments[0];
  75. path_segment.ident.clone()
  76. }
  77. _ => panic!("Invalid ProgramAccount"),
  78. },
  79. _ => panic!("Invalid ProgramAccount"),
  80. }
  81. }
  82. _ => panic!("Invalid ProgramAccount"),
  83. };
  84. ProgramAccountTy { account_ident }
  85. }
  86. fn parse_constraints(anchor: &syn::Attribute, ty: &Ty) -> (Vec<Constraint>, bool, bool) {
  87. let mut tts = anchor.tokens.clone().into_iter();
  88. let g_stream = match tts.next().expect("Must have a token group") {
  89. proc_macro2::TokenTree::Group(g) => g.stream(),
  90. _ => panic!("Invalid syntax"),
  91. };
  92. let mut is_mut = false;
  93. let mut is_signer = false;
  94. let mut constraints = vec![];
  95. let mut has_owner_constraint = false;
  96. let mut inner_tts = g_stream.into_iter();
  97. while let Some(token) = inner_tts.next() {
  98. match token {
  99. proc_macro2::TokenTree::Ident(ident) => match ident.to_string().as_str() {
  100. "mut" => {
  101. is_mut = true;
  102. }
  103. "signer" => {
  104. is_signer = true;
  105. constraints.push(Constraint::Signer(ConstraintSigner {}));
  106. }
  107. "belongs_to" => {
  108. match inner_tts.next().unwrap() {
  109. proc_macro2::TokenTree::Punct(punct) => {
  110. assert!(punct.as_char() == '=');
  111. punct
  112. }
  113. _ => panic!("invalid syntax"),
  114. };
  115. let join_target = match inner_tts.next().unwrap() {
  116. proc_macro2::TokenTree::Ident(ident) => ident,
  117. _ => panic!("invalid syntax"),
  118. };
  119. constraints.push(Constraint::BelongsTo(ConstraintBelongsTo { join_target }))
  120. }
  121. "owner" => {
  122. match inner_tts.next().unwrap() {
  123. proc_macro2::TokenTree::Punct(punct) => {
  124. assert!(punct.as_char() == '=');
  125. punct
  126. }
  127. _ => panic!("invalid syntax"),
  128. };
  129. let owner = match inner_tts.next().unwrap() {
  130. proc_macro2::TokenTree::Ident(ident) => ident,
  131. _ => panic!("invalid syntax"),
  132. };
  133. let constraint = match owner.to_string().as_str() {
  134. "program" => ConstraintOwner::Program,
  135. "skip" => ConstraintOwner::Skip,
  136. _ => panic!("invalid syntax"),
  137. };
  138. constraints.push(Constraint::Owner(constraint));
  139. has_owner_constraint = true;
  140. }
  141. _ => {
  142. panic!("invalid syntax");
  143. }
  144. },
  145. proc_macro2::TokenTree::Punct(punct) => {
  146. if punct.as_char() != ',' {
  147. panic!("invalid syntax");
  148. }
  149. }
  150. proc_macro2::TokenTree::Literal(literal) => {
  151. let tokens: proc_macro2::TokenStream =
  152. literal.to_string().replace("\"", "").parse().unwrap();
  153. constraints.push(Constraint::Literal(ConstraintLiteral { tokens }));
  154. }
  155. _ => {
  156. panic!("invalid syntax");
  157. }
  158. }
  159. }
  160. if !has_owner_constraint {
  161. if ty == &Ty::AccountInfo {
  162. constraints.push(Constraint::Owner(ConstraintOwner::Skip));
  163. } else {
  164. constraints.push(Constraint::Owner(ConstraintOwner::Program));
  165. }
  166. }
  167. (constraints, is_mut, is_signer)
  168. }