file.rs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. use crate::idl::*;
  2. use crate::parser::accounts;
  3. use crate::parser::program;
  4. use crate::AccountsStruct;
  5. use anyhow::Result;
  6. use heck::MixedCase;
  7. use quote::ToTokens;
  8. use std::collections::{HashMap, HashSet};
  9. use std::fs::File;
  10. use std::io::Read;
  11. use std::path::Path;
  12. static DERIVE_NAME: &'static str = "Accounts";
  13. // Parse an entire interface file.
  14. pub fn parse(filename: impl AsRef<Path>) -> Result<Idl> {
  15. let mut file = File::open(&filename)?;
  16. let mut src = String::new();
  17. file.read_to_string(&mut src).expect("Unable to read file");
  18. let f = syn::parse_file(&src).expect("Unable to parse file");
  19. let p = program::parse(parse_program_mod(&f));
  20. let accs = parse_accounts(&f);
  21. let acc_names = {
  22. let mut acc_names = HashSet::new();
  23. for accs_strct in accs.values() {
  24. for a in accs_strct.account_tys() {
  25. acc_names.insert(a);
  26. }
  27. }
  28. acc_names
  29. };
  30. let instructions = p
  31. .rpcs
  32. .iter()
  33. .map(|rpc| {
  34. let args = rpc
  35. .args
  36. .iter()
  37. .map(|arg| {
  38. let mut tts = proc_macro2::TokenStream::new();
  39. arg.raw_arg.ty.to_tokens(&mut tts);
  40. let ty = tts.to_string().parse().unwrap();
  41. IdlField {
  42. name: arg.name.to_string().to_mixed_case(),
  43. ty,
  44. }
  45. })
  46. .collect::<Vec<_>>();
  47. // todo: don't unwrap
  48. let accounts_strct = accs.get(&rpc.anchor_ident.to_string()).unwrap();
  49. let accounts = accounts_strct
  50. .fields
  51. .iter()
  52. .map(|acc| IdlAccount {
  53. name: acc.ident.to_string().to_mixed_case(),
  54. is_mut: acc.is_mut,
  55. is_signer: acc.is_signer,
  56. })
  57. .collect::<Vec<_>>();
  58. IdlInstruction {
  59. name: rpc.ident.to_string().to_mixed_case(),
  60. accounts,
  61. args,
  62. }
  63. })
  64. .collect::<Vec<_>>();
  65. // All user defined types.
  66. let mut accounts = vec![];
  67. let mut types = vec![];
  68. let ty_defs = parse_ty_defs(&f)?;
  69. for ty_def in ty_defs {
  70. if acc_names.contains(&ty_def.name) {
  71. accounts.push(ty_def);
  72. } else {
  73. types.push(ty_def);
  74. }
  75. }
  76. Ok(Idl {
  77. version: "0.0.0".to_string(),
  78. name: p.name.to_string(),
  79. instructions,
  80. types,
  81. accounts,
  82. metadata: None,
  83. })
  84. }
  85. // Parse the main program mod.
  86. fn parse_program_mod(f: &syn::File) -> syn::ItemMod {
  87. let mods = f
  88. .items
  89. .iter()
  90. .filter_map(|i| match i {
  91. syn::Item::Mod(item_mod) => {
  92. let mods = item_mod
  93. .attrs
  94. .iter()
  95. .filter_map(|attr| {
  96. let segment = attr.path.segments.last().unwrap();
  97. if segment.ident.to_string() == "program" {
  98. return Some(attr);
  99. }
  100. None
  101. })
  102. .collect::<Vec<_>>();
  103. if mods.len() != 1 {
  104. panic!("invalid program attribute");
  105. }
  106. Some(item_mod)
  107. }
  108. _ => None,
  109. })
  110. .collect::<Vec<_>>();
  111. assert!(mods.len() == 1);
  112. mods[0].clone()
  113. }
  114. // Parse all structs deriving the `Accounts` macro.
  115. fn parse_accounts(f: &syn::File) -> HashMap<String, AccountsStruct> {
  116. f.items
  117. .iter()
  118. .filter_map(|i| match i {
  119. syn::Item::Struct(i_strct) => {
  120. for attr in &i_strct.attrs {
  121. if attr.tokens.to_string().contains(DERIVE_NAME) {
  122. let strct = accounts::parse(i_strct);
  123. return Some((strct.ident.to_string(), strct));
  124. }
  125. }
  126. None
  127. }
  128. _ => None,
  129. })
  130. .collect()
  131. }
  132. // Parse all user defined types in the file.
  133. fn parse_ty_defs(f: &syn::File) -> Result<Vec<IdlTypeDef>> {
  134. f.items
  135. .iter()
  136. .filter_map(|i| match i {
  137. syn::Item::Struct(item_strct) => {
  138. for attr in &item_strct.attrs {
  139. if attr.tokens.to_string().contains(DERIVE_NAME) {
  140. return None;
  141. }
  142. }
  143. if let syn::Visibility::Public(_) = &item_strct.vis {
  144. let name = item_strct.ident.to_string();
  145. let fields = match &item_strct.fields {
  146. syn::Fields::Named(fields) => fields
  147. .named
  148. .iter()
  149. .map(|f| {
  150. let mut tts = proc_macro2::TokenStream::new();
  151. f.ty.to_tokens(&mut tts);
  152. Ok(IdlField {
  153. name: f.ident.as_ref().unwrap().to_string().to_mixed_case(),
  154. ty: tts.to_string().parse()?,
  155. })
  156. })
  157. .collect::<Result<Vec<IdlField>>>(),
  158. _ => panic!("Only named structs are allowed."),
  159. };
  160. return Some(fields.map(|fields| IdlTypeDef {
  161. name,
  162. ty: IdlTypeDefTy::Struct { fields },
  163. }));
  164. }
  165. None
  166. }
  167. _ => None,
  168. })
  169. .collect()
  170. }