ethbind_rust_macros/
lib.rs

1use std::{env, fs::read_to_string, path::PathBuf};
2
3use ethbind_rust::{BindingBuilder, JsonRuntimeBinder, RustGenerator, ToTokenStream};
4use proc_macro::TokenStream;
5use proc_macro2::Ident;
6use quote::quote;
7use syn::{parse::Parse, parse_macro_input, LitStr, Token};
8
9struct Contract {
10    pub contract_name: Option<String>,
11    pub type_mapping: String,
12    pub abi_data: String,
13}
14
15impl Parse for Contract {
16    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
17        let contract_name: Option<Ident> = input.parse()?;
18
19        if contract_name.is_some() {
20            input.parse::<Token!(,)>()?;
21        }
22
23        let type_mapping: LitStr = input.parse()?;
24
25        input.parse::<Token!(,)>()?;
26
27        let abi_data: LitStr = input.parse()?;
28
29        Ok(Self {
30            contract_name: contract_name.map(|c| c.to_string()),
31            type_mapping: type_mapping.value(),
32            abi_data: abi_data.value(),
33        })
34    }
35}
36
37mod contract {
38    syn::custom_keyword!(hardhat);
39}
40
41fn load_json_file(path: &str) -> String {
42    let dir = env::var("CARGO_MANIFEST_DIR").expect("Find CARGO_MANIFEST_DIR");
43
44    let path = PathBuf::from(dir).join(path);
45
46    read_to_string(path.clone()).expect(&format!("Read json file: {:?}", path))
47}
48
49#[proc_macro]
50pub fn contract(item: TokenStream) -> TokenStream {
51    let contract = parse_macro_input!(item as Contract);
52
53    let type_mapping: JsonRuntimeBinder = load_json_file(&contract.type_mapping)
54        .parse()
55        .expect("Parse mapping data");
56
57    let abi_data = load_json_file(&contract.abi_data);
58
59    let generator = if let Some(contract_name) = contract.contract_name {
60        BindingBuilder::new((RustGenerator::default(), type_mapping))
61            .bind(&contract_name, abi_data)
62            .finalize()
63            .expect("Generate contract/abi binding code")
64    } else {
65        BindingBuilder::new((RustGenerator::default(), type_mapping))
66            .bind_hardhat(abi_data)
67            .finalize()
68            .expect("Generate contract/abi binding code")
69    };
70
71    let contracts = generator.to_token_streams().expect("To token streams");
72
73    quote!(#(#contracts)*).into()
74}