fadroma_proc_derive/
lib.rs

1use quote::quote;
2use syn::{parse_macro_input, parse_quote, AttributeArgs, ItemTrait, TraitItemMethod};
3
4use crate::contract::{Contract, ContractType};
5
6mod args;
7mod attr;
8mod contract;
9mod utils;
10
11#[proc_macro_attribute]
12pub fn contract(
13    args: proc_macro::TokenStream,
14    trait_: proc_macro::TokenStream,
15) -> proc_macro::TokenStream {
16    generate_contract(args, trait_, ContractType::Contract)
17}
18
19#[proc_macro_attribute]
20pub fn interface(
21    args: proc_macro::TokenStream,
22    trait_: proc_macro::TokenStream,
23) -> proc_macro::TokenStream {
24    generate_contract(args, trait_, ContractType::Interface)
25}
26
27#[proc_macro_attribute]
28pub fn contract_impl(
29    args: proc_macro::TokenStream,
30    trait_: proc_macro::TokenStream,
31) -> proc_macro::TokenStream {
32    generate_contract(args, trait_, ContractType::Impl)
33}
34
35#[proc_macro_attribute]
36pub fn init(
37    _args: proc_macro::TokenStream,
38    func: proc_macro::TokenStream,
39) -> proc_macro::TokenStream {
40    let mut ast = parse_macro_input!(func as TraitItemMethod);
41
42    add_fn_args(&mut ast, true, false, false);
43
44    let result = quote! {
45        #ast
46    };
47
48    proc_macro::TokenStream::from(result)
49}
50
51#[proc_macro_attribute]
52pub fn execute(
53    _args: proc_macro::TokenStream,
54    func: proc_macro::TokenStream,
55) -> proc_macro::TokenStream {
56    let mut ast = parse_macro_input!(func as TraitItemMethod);
57
58    add_fn_args(&mut ast, true, false, false);
59
60    let result = quote! {
61        #ast
62    };
63
64    proc_macro::TokenStream::from(result)
65}
66
67#[proc_macro_attribute]
68pub fn query(
69    _args: proc_macro::TokenStream,
70    func: proc_macro::TokenStream,
71) -> proc_macro::TokenStream {
72    let mut ast = parse_macro_input!(func as TraitItemMethod);
73
74    add_fn_args(&mut ast, false, false, false);
75
76    let result = quote! {
77        #ast
78    };
79
80    proc_macro::TokenStream::from(result)
81}
82
83#[proc_macro_attribute]
84pub fn execute_guard(
85    _args: proc_macro::TokenStream,
86    func: proc_macro::TokenStream,
87) -> proc_macro::TokenStream {
88    let mut ast = parse_macro_input!(func as TraitItemMethod);
89
90    if ast.sig.inputs.len() != 1 {
91        let err = syn::Error::new(
92            ast.sig.paren_token.span,
93            format!(
94                "Expecting one parameter with the type: &{}",
95                contract::EXECUTE_MSG
96            ),
97        )
98        .to_compile_error();
99
100        return proc_macro::TokenStream::from(err);
101    }
102
103    add_fn_args(&mut ast, true, true, true);
104
105    let result = quote! {
106        #ast
107    };
108
109    proc_macro::TokenStream::from(result)
110}
111
112fn generate_contract(
113    args: proc_macro::TokenStream,
114    trait_: proc_macro::TokenStream,
115    ty: ContractType,
116) -> proc_macro::TokenStream {
117    let args = parse_macro_input!(args as AttributeArgs);
118    let ast = parse_macro_input!(trait_ as ItemTrait);
119
120    let item_trait = quote!(#ast);
121
122    let contract = Contract::parse(args, ast, ty);
123
124    let boilerplate = match contract {
125        Ok(contract) => contract
126            .generate_boilerplate()
127            .unwrap_or_else(|x| x.into_compile_error()),
128        Err(err) => err.to_compile_error(),
129    };
130
131    let result = quote! {
132        #item_trait
133        #boilerplate
134    };
135
136    proc_macro::TokenStream::from(result)
137}
138
139fn add_fn_args(func: &mut TraitItemMethod, is_tx: bool, ref_env: bool, ref_info: bool) {
140    func.sig.inputs.insert(0, parse_quote!(&self));
141
142    if is_tx {
143        if func.default.is_none() {
144            func.sig
145                .inputs
146                .push(parse_quote!(deps: cosmwasm_std::DepsMut));
147        } else {
148            func.sig
149                .inputs
150                .push(parse_quote!(mut deps: cosmwasm_std::DepsMut));
151        }
152        if ref_env {
153            func.sig.inputs.push(parse_quote!(env: &cosmwasm_std::Env));
154        } else {
155            func.sig.inputs.push(parse_quote!(env: cosmwasm_std::Env));
156        }
157        if ref_info {
158            func.sig
159                .inputs
160                .push(parse_quote!(info: &cosmwasm_std::MessageInfo));
161        } else {
162            func.sig
163                .inputs
164                .push(parse_quote!(info: cosmwasm_std::MessageInfo));
165        }
166    } else {
167        func.sig.inputs.push(parse_quote!(deps: cosmwasm_std::Deps));
168        func.sig.inputs.push(parse_quote!(env: cosmwasm_std::Env));
169    }
170}