anchor_client_gen_utils/generator/
mod.rs

1use std::str::FromStr;
2
3use inflector::Inflector;
4use proc_macro2::TokenStream;
5use quote::quote;
6
7use crate::idl::{FieldJsonDefinition, FieldTypeJsonDefinition, IdlJsonDefinition};
8
9pub mod events;
10pub mod instructions;
11pub mod state;
12
13pub fn generate_doc_comments(docs: Option<Vec<String>>) -> TokenStream {
14    match docs {
15        None => quote! {},
16        Some(docs) => {
17            let mut generated = TokenStream::new();
18            docs.iter().for_each(|comment| {
19                let doc = TokenStream::from_str(&format!("/// {}", comment)).unwrap();
20                generated.extend(doc);
21            });
22            generated
23        }
24    }
25}
26
27#[derive(Clone, Copy, PartialEq)]
28pub enum Module {
29    Accounts,
30    Types,
31    Instructions,
32    Events,
33}
34
35impl Module {
36    pub fn get_prefix(&self) -> String {
37        match self {
38            Self::Accounts => "crate::accounts::".to_owned(),
39            Self::Types => "crate::types::".to_owned(),
40            Self::Instructions => "crate::instructions::".to_owned(),
41            Self::Events => "crate::events::".to_owned(),
42        }
43    }
44}
45
46pub fn generate_field_type(
47    idl: &IdlJsonDefinition,
48    field_type: &FieldTypeJsonDefinition,
49    generate_for: Module,
50) -> TokenStream {
51    let generated = match field_type {
52        FieldTypeJsonDefinition::Primitive(primitive) => {
53            let primitive = TokenStream::from_str(match primitive.as_str() {
54                "publicKey" => "::anchor_lang::prelude::Pubkey",
55                "string" => "String",
56                _ => &primitive,
57            })
58            .unwrap();
59
60            quote! {
61                #primitive
62            }
63        }
64        FieldTypeJsonDefinition::Array { array } => {
65            let inner_type = array.0.clone();
66            let inner = generate_field_type(idl, &inner_type, generate_for);
67            let count = TokenStream::from_str(&array.1.to_string()).unwrap();
68
69            quote! {
70                [#inner; #count]
71            }
72        }
73        FieldTypeJsonDefinition::Defined { defined } => {
74            let pascal_case = &defined.to_pascal_case();
75            let defined = match idl.get_typedef_module(defined) {
76                None => pascal_case.clone(),
77                Some(module) => {
78                    if module == generate_for {
79                        pascal_case.clone()
80                    } else {
81                        let module_prefix = module.get_prefix();
82                        let type_with_prefix = format!("{}{}", module_prefix, pascal_case);
83                        type_with_prefix
84                    }
85                }
86            };
87            let defined = TokenStream::from_str(&defined).unwrap();
88
89            quote! {
90                #defined
91            }
92        }
93        FieldTypeJsonDefinition::Option { option } => {
94            let inner = generate_field_type(idl, option, generate_for);
95            quote! {
96                Option<#inner>
97            }
98        }
99        FieldTypeJsonDefinition::Vec { vec } => {
100            let inner = generate_field_type(idl, vec, generate_for);
101            quote! {
102                Vec<#inner>
103            }
104        }
105    };
106
107    generated
108}
109
110pub fn generate_fields(
111    idl: &IdlJsonDefinition,
112    fields: &Vec<FieldJsonDefinition>,
113    generate_for: Module,
114    is_enum_struct_field: bool,
115) -> TokenStream {
116    let mut generated = TokenStream::new();
117
118    for field in fields {
119        let name = TokenStream::from_str(&field.name.to_snake_case()).unwrap();
120        let doc = generate_doc_comments(field.docs.clone());
121        let field_type = generate_field_type(idl, &field.field_type, generate_for);
122
123        let _pub = if is_enum_struct_field {
124            quote! {}
125        } else {
126            quote! { pub }
127        };
128
129        generated.extend(quote! {
130            #doc
131            #_pub #name: #field_type,
132        });
133    }
134
135    generated
136}