abstract_macros/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
extern crate proc_macro2;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, AttributeArgs, Fields, Item};

const DELIMITER: &str = ",";

/// Add the abstract event with action to a cosmwasm_std::Response.
#[proc_macro]
pub fn with_abstract_event(input: TokenStream) -> TokenStream {
    let input = input.to_string();
    let mut input = input.split(DELIMITER);
    let base_response = input.next().unwrap().trim();
    let contract_name = input.next().unwrap().trim();
    let action = input.next().unwrap().trim();
    // Collect the remaining
    let attrs = input.collect::<Vec<&str>>().join(DELIMITER);

    let attribute_addition = if attrs.is_empty() {
        "".to_string()
    } else if attrs.starts_with('[') && attrs.ends_with(']') {
        format!(".add_attributes(vec!{attrs})")
    } else {
        format!(".add_attributes({attrs})")
    };
    let output = format!(
        "{base_response}
        .add_event(
            cosmwasm_std::Event::new(\"abstract\")
                .add_attribute(\"contract\", {contract_name})
                .add_attribute(\"action\", {action})
                {attribute_addition}
        )"
    );

    output.parse().unwrap()
}

#[proc_macro_attribute]
pub fn abstract_response(attrs: TokenStream, input: TokenStream) -> TokenStream {
    let mut item = parse_macro_input!(input as syn::Item);
    let attributes = parse_macro_input!(attrs as AttributeArgs);

    let Item::Struct(resp_struct) = &mut item else {
        panic!("Only works on structs");
    };
    let Fields::Unit = &mut resp_struct.fields else {
        panic!("Struct must be unit-struct");
    };
    let visibility = resp_struct.vis.clone();
    let resp_name = resp_struct.ident.clone();

    let contract_name = attributes[0].clone();

    let struct_def = quote!(
        #visibility struct #resp_name;
        impl #resp_name {
            #visibility fn new<T: Into<String>, A: Into<cosmwasm_std::Attribute>>(
                action: T,
                attrs: impl IntoIterator<Item = A>,
            ) -> cosmwasm_std::Response {
                cosmwasm_std::Response::new().add_event(
                    cosmwasm_std::Event::new("abstract")
                        .add_attributes(vec![("contract", #contract_name)])
                        .add_attributes(vec![("action", action)])
                        .add_attributes(attrs),
                )
            }
            #visibility fn action<T: Into<String>>(action: T) -> cosmwasm_std::Response {
                #resp_name::new(action, Vec::<cosmwasm_std::Attribute>::new())
            }
        }
    );

    struct_def.into()
}