mod.rs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. use crate::*;
  2. use syn::parse::{Error as ParseError, Result as ParseResult};
  3. use syn::punctuated::Punctuated;
  4. use syn::spanned::Spanned;
  5. use syn::token::Comma;
  6. use syn::Expr;
  7. pub mod constraints;
  8. pub fn parse(strct: &syn::ItemStruct) -> ParseResult<AccountsStruct> {
  9. let instruction_api: Option<Punctuated<Expr, Comma>> = strct
  10. .attrs
  11. .iter()
  12. .find(|a| {
  13. a.path
  14. .get_ident()
  15. .map_or(false, |ident| ident == "instruction")
  16. })
  17. .map(|ix_attr| ix_attr.parse_args_with(Punctuated::<Expr, Comma>::parse_terminated))
  18. .transpose()?;
  19. let fields = match &strct.fields {
  20. syn::Fields::Named(fields) => fields
  21. .named
  22. .iter()
  23. .map(|f| parse_account_field(f, instruction_api.is_some()))
  24. .collect::<ParseResult<Vec<AccountField>>>()?,
  25. _ => {
  26. return Err(ParseError::new_spanned(
  27. &strct.fields,
  28. "fields must be named",
  29. ))
  30. }
  31. };
  32. Ok(AccountsStruct::new(strct.clone(), fields, instruction_api))
  33. }
  34. pub fn parse_account_field(f: &syn::Field, has_instruction_api: bool) -> ParseResult<AccountField> {
  35. let ident = f.ident.clone().unwrap();
  36. let account_field = match is_field_primitive(f)? {
  37. true => {
  38. let ty = parse_ty(f)?;
  39. let (account_constraints, instruction_constraints) =
  40. constraints::parse(f, Some(&ty), has_instruction_api)?;
  41. AccountField::Field(Field {
  42. ident,
  43. ty,
  44. constraints: account_constraints,
  45. instruction_constraints,
  46. })
  47. }
  48. false => {
  49. let (account_constraints, instruction_constraints) =
  50. constraints::parse(f, None, has_instruction_api)?;
  51. AccountField::CompositeField(CompositeField {
  52. ident,
  53. constraints: account_constraints,
  54. instruction_constraints,
  55. symbol: ident_string(f)?,
  56. raw_field: f.clone(),
  57. })
  58. }
  59. };
  60. Ok(account_field)
  61. }
  62. fn is_field_primitive(f: &syn::Field) -> ParseResult<bool> {
  63. let r = matches!(
  64. ident_string(f)?.as_str(),
  65. "ProgramState"
  66. | "ProgramAccount"
  67. | "CpiAccount"
  68. | "Sysvar"
  69. | "AccountInfo"
  70. | "CpiState"
  71. | "Loader"
  72. | "Account"
  73. );
  74. Ok(r)
  75. }
  76. fn parse_ty(f: &syn::Field) -> ParseResult<Ty> {
  77. let path = match &f.ty {
  78. syn::Type::Path(ty_path) => ty_path.path.clone(),
  79. _ => return Err(ParseError::new(f.ty.span(), "invalid account type given")),
  80. };
  81. let ty = match ident_string(f)?.as_str() {
  82. "ProgramState" => Ty::ProgramState(parse_program_state(&path)?),
  83. "CpiState" => Ty::CpiState(parse_cpi_state(&path)?),
  84. "ProgramAccount" => Ty::ProgramAccount(parse_program_account(&path)?),
  85. "CpiAccount" => Ty::CpiAccount(parse_cpi_account(&path)?),
  86. "Sysvar" => Ty::Sysvar(parse_sysvar(&path)?),
  87. "AccountInfo" => Ty::AccountInfo,
  88. "Loader" => Ty::Loader(parse_program_account_zero_copy(&path)?),
  89. "Account" => Ty::Account(parse_account_ty(&path)?),
  90. _ => return Err(ParseError::new(f.ty.span(), "invalid account type given")),
  91. };
  92. Ok(ty)
  93. }
  94. fn ident_string(f: &syn::Field) -> ParseResult<String> {
  95. let path = match &f.ty {
  96. syn::Type::Path(ty_path) => ty_path.path.clone(),
  97. _ => return Err(ParseError::new(f.ty.span(), "invalid type")),
  98. };
  99. if parser::tts_to_string(&path)
  100. .replace(" ", "")
  101. .starts_with("Box<Account<")
  102. {
  103. return Ok("Account".to_string());
  104. }
  105. // TODO: allow segmented paths.
  106. if path.segments.len() != 1 {
  107. return Err(ParseError::new(
  108. f.ty.span(),
  109. "segmented paths are not currently allowed",
  110. ));
  111. }
  112. let segments = &path.segments[0];
  113. Ok(segments.ident.to_string())
  114. }
  115. fn parse_program_state(path: &syn::Path) -> ParseResult<ProgramStateTy> {
  116. let account_ident = parse_account(path)?;
  117. Ok(ProgramStateTy {
  118. account_type_path: account_ident,
  119. })
  120. }
  121. fn parse_cpi_state(path: &syn::Path) -> ParseResult<CpiStateTy> {
  122. let account_ident = parse_account(path)?;
  123. Ok(CpiStateTy {
  124. account_type_path: account_ident,
  125. })
  126. }
  127. fn parse_cpi_account(path: &syn::Path) -> ParseResult<CpiAccountTy> {
  128. let account_ident = parse_account(path)?;
  129. Ok(CpiAccountTy {
  130. account_type_path: account_ident,
  131. })
  132. }
  133. fn parse_program_account(path: &syn::Path) -> ParseResult<ProgramAccountTy> {
  134. let account_ident = parse_account(path)?;
  135. Ok(ProgramAccountTy {
  136. account_type_path: account_ident,
  137. })
  138. }
  139. fn parse_program_account_zero_copy(path: &syn::Path) -> ParseResult<LoaderTy> {
  140. let account_ident = parse_account(path)?;
  141. Ok(LoaderTy {
  142. account_type_path: account_ident,
  143. })
  144. }
  145. fn parse_account_ty(path: &syn::Path) -> ParseResult<AccountTy> {
  146. let account_type_path = parse_account(path)?;
  147. let boxed = parser::tts_to_string(&path)
  148. .replace(" ", "")
  149. .starts_with("Box<Account<");
  150. Ok(AccountTy {
  151. account_type_path,
  152. boxed,
  153. })
  154. }
  155. // TODO: this whole method is a hack. Do something more idiomatic.
  156. fn parse_account(mut path: &syn::Path) -> ParseResult<syn::TypePath> {
  157. if parser::tts_to_string(path)
  158. .replace(" ", "")
  159. .starts_with("Box<Account<")
  160. {
  161. let segments = &path.segments[0];
  162. match &segments.arguments {
  163. syn::PathArguments::AngleBracketed(args) => {
  164. // Expected: <'info, MyType>.
  165. if args.args.len() != 1 {
  166. return Err(ParseError::new(
  167. args.args.span(),
  168. "bracket arguments must be the lifetime and type",
  169. ));
  170. }
  171. match &args.args[0] {
  172. syn::GenericArgument::Type(syn::Type::Path(ty_path)) => {
  173. path = &ty_path.path;
  174. }
  175. _ => {
  176. return Err(ParseError::new(
  177. args.args[1].span(),
  178. "first bracket argument must be a lifetime",
  179. ))
  180. }
  181. }
  182. }
  183. _ => {
  184. return Err(ParseError::new(
  185. segments.arguments.span(),
  186. "expected angle brackets with a lifetime and type",
  187. ))
  188. }
  189. }
  190. }
  191. let segments = &path.segments[0];
  192. match &segments.arguments {
  193. syn::PathArguments::AngleBracketed(args) => {
  194. // Expected: <'info, MyType>.
  195. if args.args.len() != 2 {
  196. return Err(ParseError::new(
  197. args.args.span(),
  198. "bracket arguments must be the lifetime and type",
  199. ));
  200. }
  201. match &args.args[1] {
  202. syn::GenericArgument::Type(syn::Type::Path(ty_path)) => Ok(ty_path.clone()),
  203. _ => Err(ParseError::new(
  204. args.args[1].span(),
  205. "first bracket argument must be a lifetime",
  206. )),
  207. }
  208. }
  209. _ => Err(ParseError::new(
  210. segments.arguments.span(),
  211. "expected angle brackets with a lifetime and type",
  212. )),
  213. }
  214. }
  215. fn parse_sysvar(path: &syn::Path) -> ParseResult<SysvarTy> {
  216. let segments = &path.segments[0];
  217. let account_ident = match &segments.arguments {
  218. syn::PathArguments::AngleBracketed(args) => {
  219. // Expected: <'info, MyType>.
  220. if args.args.len() != 2 {
  221. return Err(ParseError::new(
  222. args.args.span(),
  223. "bracket arguments must be the lifetime and type",
  224. ));
  225. }
  226. match &args.args[1] {
  227. syn::GenericArgument::Type(syn::Type::Path(ty_path)) => {
  228. // TODO: allow segmented paths.
  229. if ty_path.path.segments.len() != 1 {
  230. return Err(ParseError::new(
  231. ty_path.path.span(),
  232. "segmented paths are not currently allowed",
  233. ));
  234. }
  235. let path_segment = &ty_path.path.segments[0];
  236. path_segment.ident.clone()
  237. }
  238. _ => {
  239. return Err(ParseError::new(
  240. args.args[1].span(),
  241. "first bracket argument must be a lifetime",
  242. ))
  243. }
  244. }
  245. }
  246. _ => {
  247. return Err(ParseError::new(
  248. segments.arguments.span(),
  249. "expected angle brackets with a lifetime and type",
  250. ))
  251. }
  252. };
  253. let ty = match account_ident.to_string().as_str() {
  254. "Clock" => SysvarTy::Clock,
  255. "Rent" => SysvarTy::Rent,
  256. "EpochSchedule" => SysvarTy::EpochSchedule,
  257. "Fees" => SysvarTy::Fees,
  258. "RecentBlockhashes" => SysvarTy::RecentBlockhashes,
  259. "SlotHashes" => SysvarTy::SlotHashes,
  260. "SlotHistory" => SysvarTy::SlotHistory,
  261. "StakeHistory" => SysvarTy::StakeHistory,
  262. "Instructions" => SysvarTy::Instructions,
  263. "Rewards" => SysvarTy::Rewards,
  264. _ => {
  265. return Err(ParseError::new(
  266. account_ident.span(),
  267. "invalid sysvar provided",
  268. ))
  269. }
  270. };
  271. Ok(ty)
  272. }