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}