nibiru_ownable_derive/lib.rs
1extern crate proc_macro;
2extern crate quote;
3extern crate syn;
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{parse_macro_input, AttributeArgs, DataEnum, DeriveInput};
7
8/// Merges the variants of two enums.
9///
10/// Adapted from DAO DAO:
11/// https://github.com/DA0-DA0/dao-contracts/blob/74bd3881fdd86829e5e8b132b9952dd64f2d0737/packages/dao-macros/src/lib.rs#L9
12fn merge_variants(
13 metadata: TokenStream,
14 left: TokenStream,
15 right: TokenStream,
16) -> TokenStream {
17 use syn::Data::Enum;
18
19 // parse metadata
20 let args = parse_macro_input!(metadata as AttributeArgs);
21 if let Some(first_arg) = args.first() {
22 return syn::Error::new_spanned(first_arg, "macro takes no arguments")
23 .to_compile_error()
24 .into();
25 }
26
27 // parse the left enum
28 let mut left: DeriveInput = parse_macro_input!(left);
29 let Enum(DataEnum { variants, .. }) = &mut left.data else {
30 return syn::Error::new(
31 left.ident.span(),
32 "only enums can accept variants",
33 )
34 .to_compile_error()
35 .into();
36 };
37
38 // parse the right enum
39 let right: DeriveInput = parse_macro_input!(right);
40 let Enum(DataEnum {
41 variants: to_add, ..
42 }) = right.data
43 else {
44 return syn::Error::new(
45 left.ident.span(),
46 "only enums can provide variants",
47 )
48 .to_compile_error()
49 .into();
50 };
51
52 // insert variants from the right to the left
53 variants.extend(to_add);
54
55 quote! { #left }.into()
56}
57
58/// Append ownership-related execute message variant(s) to an enum.
59///
60/// For example, apply the `ownable_execute` macro to the following enum:
61///
62/// ```rust
63/// extern crate cosmwasm_schema; // not to be copied
64/// extern crate nibiru_ownable; // not to be copied
65/// use cosmwasm_schema::cw_serde;
66/// use nibiru_ownable::ownable_execute;
67///
68/// #[ownable_execute]
69/// #[cw_serde]
70/// enum ExecuteMsg {
71/// Foo {},
72/// Bar {},
73/// }
74/// ```
75///
76/// Is equivalent to:
77///
78/// ```rust
79/// extern crate cosmwasm_schema; // not to be copied
80/// extern crate nibiru_ownable; // not to be copied
81/// use cosmwasm_schema::cw_serde;
82/// use nibiru_ownable::Action;
83///
84/// #[cw_serde]
85/// enum ExecuteMsg {
86/// UpdateOwnership(Action),
87/// Foo {},
88/// Bar {},
89/// }
90///
91/// let _msg = ExecuteMsg::Foo{};
92/// ```
93///
94/// Note: `#[ownable_execute]` must be applied _before_ `#[cw_serde]`.
95#[proc_macro_attribute]
96pub fn ownable_execute(
97 metadata: TokenStream,
98 input: TokenStream,
99) -> TokenStream {
100 merge_variants(
101 metadata,
102 input,
103 quote! {
104 enum Right {
105 /// Update the contract's ownership. The `action` to be provided
106 /// can be either to propose transferring ownership to an account,
107 /// accept a pending ownership transfer, or renounce the ownership
108 /// permanently.
109 UpdateOwnership(::nibiru_ownable::Action),
110 }
111 }
112 .into(),
113 )
114}
115
116/// Append ownership-related query message variant(s) to an enum.
117///
118/// For example, apply the `ownable_query` macro to the following enum:
119///
120/// ```rust
121/// extern crate cosmwasm_schema; // not to be copied
122/// extern crate nibiru_ownable; // not to be copied
123/// use cosmwasm_schema::{cw_serde, QueryResponses};
124/// use nibiru_ownable::ownable_query;
125///
126/// #[ownable_query]
127/// #[cw_serde]
128/// #[derive(QueryResponses)]
129/// enum QueryMsg {
130/// #[returns(FooResponse)]
131/// Foo {},
132/// #[returns(BarResponse)]
133/// Bar {},
134/// }
135///
136/// #[cw_serde]
137/// pub struct FooResponse {}
138/// #[cw_serde]
139/// pub struct BarResponse {}
140///
141/// let _msg = QueryMsg::Foo{};
142/// ```
143///
144/// Is equivalent to:
145///
146/// ```rust
147/// extern crate cosmwasm_schema; // not to be copied
148/// extern crate nibiru_ownable; // not to be copied
149/// use cosmwasm_schema::{cw_serde, QueryResponses};
150/// use nibiru_ownable::Ownership;
151///
152/// #[cw_serde]
153/// #[derive(QueryResponses)]
154/// enum QueryMsg {
155/// #[returns(Ownership<String>)]
156/// Ownership {},
157/// #[returns(FooResponse)]
158/// Foo {},
159/// #[returns(BarResponse)]
160/// Bar {},
161/// }
162///
163/// #[cw_serde]
164/// pub struct FooResponse {}
165/// #[cw_serde]
166/// pub struct BarResponse {}
167///
168/// let _msg = QueryMsg::Bar{};
169/// ```
170///
171/// Note: `#[ownable_query]` must be applied _before_ `#[cw_serde]`.
172#[proc_macro_attribute]
173pub fn ownable_query(metadata: TokenStream, input: TokenStream) -> TokenStream {
174 merge_variants(
175 metadata,
176 input,
177 quote! {
178 enum Right {
179 /// Query the contract's ownership information
180 #[returns(::nibiru_ownable::Ownership<String>)]
181 Ownership {},
182 }
183 }
184 .into(),
185 )
186}