clockwork_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_ix_handlers, generate_ix_structs, generate_typedefs, GEN_VERSION,
13};
14
15#[derive(Default, FromMeta)]
16pub struct GeneratorOptions {
17    /// Path to the IDL.
18    pub idl_path: String,
19    /// List of zero copy structs.
20    pub zero_copy: Option<PathList>,
21    /// List of `repr(packed)` structs.
22    pub packed: Option<PathList>,
23}
24
25fn path_list_to_string(list: Option<&PathList>) -> HashSet<String> {
26    list.map(|el| {
27        el.iter()
28            .map(|el| el.get_ident().unwrap().to_string())
29            .collect()
30    })
31    .unwrap_or_default()
32}
33
34impl GeneratorOptions {
35    pub fn to_generator(&self) -> Generator {
36        let cargo_manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
37        let path = PathBuf::from(cargo_manifest_dir).join(&self.idl_path);
38        let idl_contents = fs::read_to_string(&path).unwrap();
39        let idl: anchor_syn::idl::Idl = serde_json::from_str(&idl_contents).unwrap();
40
41        let zero_copy = path_list_to_string(self.zero_copy.as_ref());
42        let packed = path_list_to_string(self.packed.as_ref());
43
44        let mut struct_opts: BTreeMap<String, StructOpts> = BTreeMap::new();
45        let all_structs: HashSet<&String> = zero_copy.union(&packed).collect::<HashSet<_>>();
46        all_structs.into_iter().for_each(|name| {
47            struct_opts.insert(
48                name.to_string(),
49                StructOpts {
50                    zero_copy: zero_copy.contains(name),
51                    packed: packed.contains(name),
52                },
53            );
54        });
55
56        Generator { idl, struct_opts }
57    }
58}
59
60#[derive(Clone, Copy, Default)]
61pub struct StructOpts {
62    pub packed: bool,
63    pub zero_copy: bool,
64}
65
66pub struct Generator {
67    pub idl: anchor_syn::idl::Idl,
68    pub struct_opts: BTreeMap<String, StructOpts>,
69}
70
71impl Generator {
72    pub fn generate_cpi_interface(&self) -> TokenStream {
73        let idl = &self.idl;
74        let program_name: Ident = format_ident!("{}", idl.name);
75
76        let accounts = generate_accounts(&idl.types, &idl.accounts, &self.struct_opts);
77        let typedefs = generate_typedefs(&idl.types, &self.struct_opts);
78        let ix_handlers = generate_ix_handlers(&idl.instructions);
79        let ix_structs = generate_ix_structs(&idl.instructions);
80
81        let docs = format!(
82        " Anchor CPI crate generated from {} v{} using [anchor-gen](https://crates.io/crates/anchor-gen) v{}.",
83        &idl.name,
84        &idl.version,
85        &GEN_VERSION.unwrap_or("unknown")
86    );
87
88        quote! {
89            use anchor_lang::prelude::*;
90
91            pub mod typedefs {
92                //! User-defined types.
93                use super::*;
94                #typedefs
95            }
96
97            pub mod state {
98                //! Structs of accounts which hold state.
99                use super::*;
100                #accounts
101            }
102
103            pub mod ix_accounts {
104                //! Accounts used in instructions.
105                use super::*;
106                #ix_structs
107            }
108
109            use ix_accounts::*;
110            pub use state::*;
111            pub use typedefs::*;
112
113            #[program]
114            pub mod #program_name {
115                #![doc = #docs]
116
117                use super::*;
118                #ix_handlers
119            }
120        }
121    }
122}