Skip to main content

hpl_toolkit_derive_gen_idl/
lib.rs

1extern crate proc_macro;
2use proc_macro::TokenStream;
3#[cfg(feature = "idl-build")]
4use {
5    proc_macro2::TokenStream as TokenStream2,
6    regex::Regex
7};
8
9#[proc_macro_derive(GenIdl)]
10pub fn gen_idl(_item: TokenStream) -> TokenStream {
11    #[cfg(feature = "idl-build")]
12    {
13        use anchor_syn::idl::*;
14        println!("idl-build");
15        let parsed: syn::Item = syn::parse(_item).unwrap();
16        let name;
17        let idl_build_impl = match &parsed {
18            syn::Item::Struct(item) => {
19                name = &item.ident;
20                println!("parsed: {:?}", name);
21                impl_idl_build_struct(item)
22            }
23            syn::Item::Enum(item) => {
24                name = &item.ident;
25                println!("parsed: {:?}", name);
26                impl_idl_build_enum(item)
27            }
28            syn::Item::Union(item) => {
29                name = &item.ident;
30                println!("parsed: {:?}", name);
31                impl_idl_build_union(item)
32            }
33            // Derive macros can only be defined on structs, enums, and unions.
34            _ => unreachable!(),
35        };
36        println!("idl_build_impl: {:?}", name);
37
38        return modify_insert_calls(idl_build_impl).into();
39    };
40
41    #[allow(unreachable_code)]
42    TokenStream::new()
43}
44
45#[cfg(feature = "idl-build")]
46// This function modifies the generated TokenStream to wrap insert calls with an if statement if it's not already there
47fn modify_insert_calls(input: TokenStream2) -> TokenStream2 {
48
49    // Convert the input TokenStream2 to a string
50    let input_str = input.to_string();
51
52    // Define a regex to match _types.insert("name", ...)
53    let re = Regex::new(r#"(?s)if let Some\((\w+)\)\s*=\s*<\s*([^>]+)\s*>\s*::\s*create_type\(\)\s*\{\s*types\.insert\(<\s*([^>]+)\s*>\s*::\s*get_full_path\(\),\s*(\w+)\);\s*<\s*([^>]+)\s*>\s*::\s*insert_types\(types\);\s*\}"#).unwrap();
54    // Iterate through all matches of _types.insert("name", ...)
55    let modified_str = re.replace_all(&input_str, |caps: &regex::Captures| {
56        let ty = caps[1].trim(); // The "name" captured from the first argument of insert
57        let name = caps[2].trim(); // The "name" captured from the first argument of insert
58        // println!("// CHECK THIS OUT NOW: {:?}", name);
59
60        if name == "Self" {
61           return "/* Replaced Self */".to_string();
62        }
63        // Return the new code with an if condition around insert
64        format!(
65            r#"if let Some({0}) = < {1} > :: create_type()
66        {{
67            types.insert(< {1} > :: get_full_path(), {0}); < {1} > ::
68            insert_types(types);
69        }}"#,
70            ty, name
71        )
72    });
73
74    // println!("/* CHECK THIS OUT: {:?} */", modified_str);
75
76    // Convert the modified string back to TokenStream22
77    let modified_output: TokenStream2 = modified_str.parse().unwrap();
78
79    // Return the modified TokenStream2
80    modified_output
81}