anchor_idl/
program.rs

1use std::{
2    collections::{BTreeMap, HashSet},
3    env, fs,
4    path::PathBuf,
5};
6
7use darling::{util::PathList, FromMeta};
8use proc_macro2::{Ident, TokenStream};
9use quote::{format_ident, quote};
10
11use crate::{
12    generate_accounts, generate_events, generate_ix_handlers, generate_ix_structs,
13    generate_typedefs, GEN_VERSION,
14};
15
16#[derive(Default, FromMeta)]
17pub struct GeneratorOptions {
18    /// Path to the IDL.
19    pub idl_path: String,
20    /// List of types to skip from generation. These should be provided by the caller instead.
21    pub skip: Option<PathList>,
22    /// List of zero copy structs.
23    pub zero_copy: Option<PathList>,
24    /// List of `repr(packed)` structs.
25    pub packed: Option<PathList>,
26}
27
28fn path_list_to_string(list: Option<&PathList>) -> HashSet<String> {
29    list.map(|el| {
30        el.iter()
31            .map(|el| el.get_ident().unwrap().to_string())
32            .collect()
33    })
34    .unwrap_or_default()
35}
36
37impl GeneratorOptions {
38    pub fn to_generator(&self) -> Generator {
39        let cargo_manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
40        let path = PathBuf::from(cargo_manifest_dir).join(&self.idl_path);
41        let idl_contents = fs::read_to_string(&path).unwrap();
42        let idl: anchor_lang_idl_spec::Idl = serde_json::from_str(&idl_contents).unwrap();
43
44        let skip = path_list_to_string(self.skip.as_ref());
45        let zero_copy = path_list_to_string(self.zero_copy.as_ref());
46        let packed = path_list_to_string(self.packed.as_ref());
47
48        let all_type_names = idl
49            .accounts
50            .iter()
51            .map(|a| a.name.clone())
52            .chain(idl.types.iter().map(|t| t.name.clone()))
53            .collect::<HashSet<_>>();
54
55        let mut struct_opts: BTreeMap<String, StructOpts> = BTreeMap::new();
56        all_type_names.iter().for_each(|name| {
57            struct_opts.insert(
58                name.to_string(),
59                StructOpts {
60                    skip: skip.contains(name),
61                    zero_copy: zero_copy.contains(name),
62                    packed: packed.contains(name),
63                },
64            );
65        });
66
67        Generator { idl, struct_opts }
68    }
69}
70
71#[derive(Clone, Copy, Debug, Default)]
72pub struct StructOpts {
73    pub skip: bool,
74    pub packed: bool,
75    pub zero_copy: bool,
76}
77
78pub struct Generator {
79    pub idl: anchor_lang_idl_spec::Idl,
80    pub struct_opts: BTreeMap<String, StructOpts>,
81}
82
83impl Generator {
84    pub fn generate_cpi_interface(&self) -> TokenStream {
85        let idl = &self.idl;
86        let program_name: Ident = format_ident!("{}", idl.metadata.name);
87
88        let accounts = generate_accounts(&idl.types, &idl.accounts, &self.struct_opts);
89        let events = generate_events(&idl.events, &idl.types, &self.struct_opts);
90        let typedefs = generate_typedefs(&idl.types, &self.struct_opts);
91        let ix_handlers = generate_ix_handlers(&idl.instructions);
92        let ix_structs = generate_ix_structs(&idl.instructions);
93
94        let docs = format!(
95        " Anchor CPI crate generated from {} v{} using [anchor-gen](https://crates.io/crates/anchor-gen) v{}.",
96        &idl.metadata.name,
97        &idl.metadata.version,
98        &GEN_VERSION.unwrap_or("unknown")
99    );
100
101        let address = idl.address.clone();
102
103        quote! {
104            use anchor_lang::prelude::*;
105
106            declare_id!(#address);
107
108            pub mod typedefs {
109                //! User-defined types.
110                use super::*;
111                #typedefs
112            }
113
114            pub mod state {
115                //! Structs of accounts which hold state.
116                use super::*;
117                #accounts
118            }
119
120            pub mod events {
121                //! Structs of events generated by program.
122                use super::*;
123                #events
124            }
125
126            pub mod ix_accounts {
127                //! Accounts used in instructions.
128                use super::*;
129                #ix_structs
130            }
131
132            use ix_accounts::*;
133            pub use state::*;
134            pub use typedefs::*;
135
136            #[program]
137            pub mod #program_name {
138                #![doc = #docs]
139
140                use super::*;
141                #ix_handlers
142            }
143        }
144    }
145}