Skip to content

Commit

Permalink
feat(axelar-wasm-std-derive-minor): add generics support for into-eve…
Browse files Browse the repository at this point in the history
…nt derive macro
  • Loading branch information
fish-sammy committed Jan 9, 2025
1 parent a7cb956 commit 576f126
Showing 1 changed file with 101 additions and 14 deletions.
115 changes: 101 additions & 14 deletions packages/axelar-wasm-std-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use itertools::Itertools;
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
use quote::quote;
use syn::{DeriveInput, FieldsNamed, ItemEnum, Variant};
use syn::{DeriveInput, FieldsNamed, Generics, ItemEnum, Variant};

#[proc_macro_derive(IntoContractError)]
pub fn into_contract_error_derive(input: TokenStream) -> TokenStream {
Expand Down Expand Up @@ -33,6 +33,7 @@ pub fn into_contract_error_derive(input: TokenStream) -> TokenStream {
/// ```
/// use std::collections::BTreeMap;
/// use serde::Serialize;
/// use cosmwasm_std::Event;
///
/// use axelar_wasm_std_derive::IntoEvent;
///
Expand All @@ -45,7 +46,10 @@ pub fn into_contract_error_derive(input: TokenStream) -> TokenStream {
/// }
///
/// #[derive(IntoEvent)]
/// enum SomeEvents {
/// enum SomeEvents<T, U> where
/// T: Serialize,
/// U: Serialize
/// {
/// SomeEmptyEvent,
/// SomeOtherEmptyEvent {},
/// SomeEvent {
Expand All @@ -54,17 +58,21 @@ pub fn into_contract_error_derive(input: TokenStream) -> TokenStream {
/// some_bool: bool,
/// some_object: SomeObject,
/// },
/// SomeGenericsEvent {
/// some_generics: T,
/// some_other_generics: U,
/// }
/// }
///
/// let actual = cosmwasm_std::Event::from(SomeEvents::SomeEmptyEvent);
/// let expected = cosmwasm_std::Event::new("some_empty_event");
/// let actual: Event = SomeEvents::SomeEmptyEvent.non_generic().into();
/// let expected = Event::new("some_empty_event");
/// assert_eq!(actual, expected);
///
/// let actual = cosmwasm_std::Event::from(SomeEvents::SomeOtherEmptyEvent {});
/// let expected = cosmwasm_std::Event::new("some_other_empty_event");
/// let actual: Event = SomeEvents::SomeOtherEmptyEvent {}.non_generic().into();
/// let expected = Event::new("some_other_empty_event");
/// assert_eq!(actual, expected);
///
/// let actual = cosmwasm_std::Event::from(SomeEvents::SomeEvent {
/// let actual: Event = SomeEvents::SomeEvent {
/// some_uint: 42,
/// some_string: "some string".to_string(),
/// some_bool: true,
Expand All @@ -74,13 +82,22 @@ pub fn into_contract_error_derive(input: TokenStream) -> TokenStream {
/// some_vec: vec!["a".to_string(), "b".to_string()],
/// some_map: [("a".to_string(), "b".to_string()), ("c".to_string(), "d".to_string()), ("e".to_string(), "f".to_string())].into_iter().collect(),
/// },
/// });
/// let expected = cosmwasm_std::Event::new("some_event")
/// }.non_generic().into();
/// let expected = Event::new("some_event")
/// .add_attribute("some_uint", "42")
/// .add_attribute("some_string", "\"some string\"")
/// .add_attribute("some_bool", "true")
/// .add_attribute("some_object", "{\"some_option\":\"some option\",\"some_other_option\":null,\"some_vec\":[\"a\",\"b\"],\"some_map\":{\"a\":\"b\",\"c\":\"d\",\"e\":\"f\"}}");
/// assert_eq!(actual, expected);
///
/// let actual: Event = SomeEvents::SomeGenericsEvent {
/// some_generics: "some generics".to_string(),
/// some_other_generics: 42,
/// }.into();
/// let expected = Event::new("some_generics_event")
/// .add_attribute("some_generics", "\"some generics\"")
/// .add_attribute("some_other_generics", "42");
/// assert_eq!(actual, expected);
/// ```
///
/// ```compile_fail
Expand All @@ -102,22 +119,60 @@ pub fn into_contract_error_derive(input: TokenStream) -> TokenStream {
/// # Unnamed(u64),
/// # }
/// ```
///
/// ```compile_fail
/// # use axelar_wasm_std_derive::IntoEvent;
///
/// # #[derive(IntoEvent)] // should not compile because the event enum has no variants
/// # enum SomeEmptyEvent {}
/// ```
///
/// ```compile_fail
/// # use axelar_wasm_std_derive::IntoEvent;
///
/// # #[derive(IntoEvent)] // should not compile because const generics are not supported
/// # enum SomeConstGenericEvent<const N: usize> {
/// # SomeConstGenericEvent { some_array: [u8; N] },
/// # }
/// ```
///
/// ```compile_fail
/// # use axelar_wasm_std_derive::IntoEvent;
///
/// # #[derive(IntoEvent)] // should not compile because lifetime generics are not supported
/// # enum SomeLifetimeGenericEvent<'a> {
/// # SomeLifetimeGenericEvent { some_str: &'a str },
/// # }
/// ```
///
/// ```compile_fail
/// # use axelar_wasm_std_derive::IntoEvent;
///
/// # #[derive(IntoEvent)]
/// # enum SomeEventWithoutGenerics {
/// # SomeEvent
/// # }
///
/// # let _ = SomeEventWithoutGenerics::SomeEvent.non_generic(); // should not compile because the event enum has no generics
/// ```
#[proc_macro_derive(IntoEvent)]
pub fn into_event(input: TokenStream) -> TokenStream {
let ItemEnum {
variants,
ident: event_enum,
generics,
..
} = syn::parse_macro_input!(input as syn::ItemEnum);

try_into_event(event_enum, variants)
try_into_event(event_enum, variants, generics)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}

fn try_into_event(
event_enum: Ident,
variants: impl IntoIterator<Item = Variant>,
generics: Generics,
) -> Result<TokenStream2, syn::Error> {
let variant_matches: Vec<_> = variants
.into_iter()
Expand All @@ -134,24 +189,56 @@ fn try_into_event(
)),
})
.try_collect()?;
if variant_matches.is_empty() {
return Err(syn::Error::new(Span::call_site(), "no variants found"));
}

if generics.lifetimes().count() > 0 || generics.const_params().count() > 0 {
return Err(syn::Error::new(
Span::call_site(),
"lifetimes and const generics are not supported",
));
}

let non_generic = impl_non_generic(&event_enum, &generics);
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();

Ok(quote! {
impl From<&#event_enum> for cosmwasm_std::Event {
fn from(event: &#event_enum) -> Self {
#non_generic

impl #impl_generics From<&#event_enum #type_generics> for cosmwasm_std::Event #where_clause {
fn from(event: &#event_enum #type_generics) -> Self {
match event {
#(#variant_matches),*
}
}
}

impl From<#event_enum> for cosmwasm_std::Event {
fn from(event: #event_enum) -> Self {
impl #impl_generics From<#event_enum #type_generics> for cosmwasm_std::Event #where_clause {
fn from(event: #event_enum #type_generics) -> Self {
(&event).into()
}
}
})
}

fn impl_non_generic(event_enum: &Ident, generics: &Generics) -> TokenStream2 {
let typed_generic_param_count = generics.type_params().count();
if typed_generic_param_count == 0 {
return quote! {};
}

let empties = (0..typed_generic_param_count).map(|_| quote! { cosmwasm_std::Empty });

quote! {
impl #event_enum<#(#empties), *> {
pub fn non_generic(self) -> Self {
self
}
}
}
}

fn match_structured_variant(
event_enum: &Ident,
variant_name: &Ident,
Expand Down

0 comments on commit 576f126

Please sign in to comment.