cw_iper_test_macros/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::TokenTree;
3use quote::quote;
4use syn::{parse_macro_input, DeriveInput, Expr, Lit, Meta};
5
6/// Implements following traits from `cw_iper_test` crate:
7/// - `IbcPortInterface`
8#[proc_macro_derive(IbcPort, attributes(ibc_port))]
9pub fn derive_ibc_port(input: TokenStream) -> TokenStream {
10    let input = parse_macro_input!(input as DeriveInput);
11
12    let struct_name = &input.ident;
13
14    let port = get_attr("ibc_port", &input.attrs).expect("ibc_port attribute not found");
15
16    let mut f: String = "".to_string();
17
18    if let Meta::NameValue(a) = &port.meta {
19        if let Expr::Lit(b) = &a.value {
20            if let Lit::Str(c) = &b.lit {
21                f = c.value();
22            }
23        }
24    };
25
26    if f == *"" {
27        panic!("port attributes not in corrected format. Requested in format #[port = 'port']")
28    }
29
30    let prepath = prepath();
31
32    let expanded = quote! {
33        impl #struct_name {
34            /// Ibc port name
35            pub const IBC_PORT: &'static str = #f;
36        }
37        impl #prepath::IbcPortInterface for #struct_name {
38            fn port_name(&self) -> String {
39                #f.to_string()
40            }
41        }
42    };
43
44    TokenStream::from(expanded)
45}
46
47/// Implements following traits from `cw_iper_test` crate:
48/// - `StargateUrls`
49/// - `StargateName`
50#[proc_macro_derive(Stargate, attributes(stargate))]
51pub fn derive_stargate(input: TokenStream) -> TokenStream {
52    let input = parse_macro_input!(input as DeriveInput);
53    let struct_name = &input.ident;
54
55    let attributes = get_attr("stargate", &input.attrs).expect("stargate attribute not found");
56
57    let mut query = None;
58
59    let mut msgs = None;
60
61    let mut name = None;
62
63    if let Meta::List(list) = &attributes.meta {
64        for (index, token) in list.tokens.clone().into_iter().enumerate() {
65            if let TokenTree::Ident(ident) = token {
66                if ident == "name" {
67                    let a: Vec<TokenTree> = list.tokens.clone().into_iter().collect();
68                    let a = a[index + 2].clone();
69                    name = Some(quote! {#a})
70                }
71                if ident == "query_urls" {
72                    let a: Vec<TokenTree> = list.tokens.clone().into_iter().collect();
73                    let a = a[index + 2].clone();
74                    query = Some(quote! {#a})
75                }
76
77                if ident == "msgs_urls" {
78                    let a: Vec<TokenTree> = list.tokens.clone().into_iter().collect();
79                    let a = a[index + 2].clone();
80                    msgs = Some(quote! {#a})
81                }
82            }
83        }
84    }
85
86    let query = query.expect("query_urls attribute not found");
87    let msgs = msgs.expect("msgs_urls attribute not found");
88
89    let prepath = prepath();
90
91    let expanded = quote! {
92        impl #prepath::StargateUrls for #struct_name {
93
94            fn is_query_type_url(&self, type_url: String) -> bool {
95                <#query as std::str::FromStr>::from_str(&type_url).is_ok()
96            }
97
98            fn is_msg_type_url(&self, type_url: String) -> bool {
99                <#msgs as std::str::FromStr>::from_str(&type_url).is_ok()
100            }
101
102            fn type_urls(&self) -> Vec<String> {
103                let mut urls = Vec::new();
104                urls.extend(<#query as #prepath::strum::IntoEnumIterator>::iter().map(|url| url.to_string()));
105                urls.extend(<#msgs as #prepath::strum::IntoEnumIterator>::iter().map(|url| url.to_string()));
106                urls
107            }
108        }
109
110        impl #prepath::StargateName for #struct_name {
111            fn stargate_name(&self) -> String {
112                #name.to_string()
113            }
114        }
115    };
116
117    TokenStream::from(expanded)
118}
119
120/// Implements following derive:
121///
122/// ```ignore
123/// // Example
124/// #[derive(strum_macros::EnumString, strum_macros::EnumIter, strum_macros::Display)]
125/// pub enum Ics20MsgUrls {
126///     #[strum(serialize = "/ibc.applications.transfer.v1.MsgTransfer")]
127///     MsgTransfer,
128///     ... // Others enum fields
129/// }
130#[proc_macro_attribute]
131pub fn urls(_attr: proc_macro::TokenStream, input: proc_macro::TokenStream) -> TokenStream {
132    let input = parse_macro_input!(input as DeriveInput);
133
134    let prepath = prepath();
135
136    let expanded = quote! {
137        #[derive(
138            #prepath::strum_macros::EnumString,
139            #prepath::strum_macros::EnumIter,
140            #prepath::strum_macros::Display
141        )]
142        #input
143    };
144    TokenStream::from(expanded)
145}
146
147fn get_attr<'a>(attr_ident: &str, attrs: &'a [syn::Attribute]) -> Option<&'a syn::Attribute> {
148    attrs.iter().find(|&attr| {
149        attr.path().segments.len() == 1 && attr.path().segments[0].ident == attr_ident
150    })
151}
152
153#[allow(unreachable_code)]
154fn is_internal() -> bool {
155    #[cfg(feature = "internal")]
156    {
157        return true;
158    }
159    false
160}
161
162fn prepath() -> proc_macro2::TokenStream {
163    if is_internal() {
164        quote! {crate}
165    } else {
166        quote! {cw_iper_test}
167    }
168}