Skip to content

Commit

Permalink
feat: Add derive macro "IntoEvent" (#161)
Browse files Browse the repository at this point in the history
* feat: add IntoEvent derive macro

* feat: make `add_event` and `add_events` able to takes types implements `IntoEvent`

* move IntoEvent macro to cosmwasm-derive

* feat: add attribute "use_to_string" to IntoEvent derive macro

* chore: delete unused files

* chore: update contracts' Cargo.lock
  • Loading branch information
loloicci authored Mar 2, 2022
1 parent 46c9ac0 commit 646b24e
Show file tree
Hide file tree
Showing 18 changed files with 579 additions and 1,499 deletions.
336 changes: 170 additions & 166 deletions Cargo.lock

Large diffs are not rendered by default.

1,327 changes: 0 additions & 1,327 deletions MIGRATING.md

This file was deleted.

9 changes: 9 additions & 0 deletions contracts/burner/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions contracts/crypto-verify/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions contracts/floaty/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions contracts/hackatom/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions contracts/ibc-reflect-send/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions contracts/ibc-reflect/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions contracts/queue/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions contracts/reflect/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions contracts/staking/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions contracts/voting-with-uuid/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ default = []

[dependencies]
syn = { version = "1.0", features = ["full"] }
quote = "1.0"
proc-macro2 = "1.0.32"
convert_case = "0.4.0"

[dev-dependencies]
# Needed for testing docs
Expand Down
110 changes: 110 additions & 0 deletions packages/derive/src/into_event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
extern crate proc_macro;

use convert_case::{Case, Casing};
use proc_macro::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
use syn::{Attribute, DataStruct, DeriveInput, Error, Field, Ident, Result};

/// scan attrs and get `to_string_fn` attribute
fn scan_to_string_fn(field: &Field) -> Result<Option<proc_macro2::TokenStream>> {
let filtered: Vec<&Attribute> = field
.attrs
.iter()
.filter(|a| a.path.is_ident("to_string_fn"))
.collect();
if filtered.len() > 1 {
return Err(Error::new(
field.span(),
"[IntoEvent] Only one or zero `to_string_fn` can be applied to one field.",
));
};
if filtered.is_empty() {
Ok(None)
} else {
Ok(Some(filtered[0].tokens.clone()))
}
}

/// scan attrs and return if it has any `to_string`
fn has_use_to_string(field: &Field) -> Result<bool> {
let mut filtered = field
.attrs
.iter()
.filter(|a| a.path.is_ident("use_to_string"));
if filtered.clone().any(|a| !a.tokens.is_empty()) {
return Err(Error::new(
field.span(),
"[IntoEvent] attribute `use_to_string` has some value. If you intend to specify the cast function to string, use `to_string_fn` instead.",
));
}
Ok(filtered.next().is_some())
}

/// generate an ast for `impl Into<cosmwasm::Event>` from a struct
fn make_init_from_struct(id: Ident, struct_data: DataStruct) -> Result<proc_macro2::TokenStream> {
// snake case of struct ident
let name = id.to_string().as_str().to_case(Case::Snake);

// generate the body of `fn into`
// generating `Event::new()` part
let mut fn_body = quote!(
cosmwasm_std::Event::new(#name)
);

// chain `.add_attribute`s to `Event::new()` part
for field in struct_data.fields {
let field_id = match field.clone().ident {
None => {
return Err(Error::new(
field.span(),
"[IntoEvent] Unexpected unnamed field.",
))
}
Some(field_id) => field_id,
};
let value = match (scan_to_string_fn(&field)?, has_use_to_string(&field)?) {
(Some(_), true) => return Err(Error::new(
field.span(),
"[IntoEvent] Both `use_to_string` and `to_string_fn` are applied to an field. Only one can be applied.",
)),
(Some(to_string_fn), false) => quote!(#to_string_fn(self.#field_id)),
(None, true) => quote!(self.#field_id.to_string()),
(None, false) => quote!(self.#field_id),
};
fn_body.extend(quote!(
.add_attribute(stringify!(#field_id), #value)
))
}

// generate the `impl Into<cosmwasm_std::Event>` from generated `fn_body`
let gen = quote!(
impl Into<cosmwasm_std::Event> for #id {
fn into(self) -> cosmwasm_std::Event {
#fn_body
}
}
);
Ok(gen)
}

/// derive `IntoEvent` from a derive input. The input needs to be a struct.
pub fn derive_into_event(input: DeriveInput) -> TokenStream {
match input.data {
syn::Data::Struct(struct_data) => make_init_from_struct(input.ident, struct_data)
.unwrap_or_else(|e| e.to_compile_error())
.into(),
syn::Data::Enum(enum_data) => Error::new(
enum_data.enum_token.span,
"[IntoEvent] `derive(IntoEvent)` cannot be applied to Enum.",
)
.to_compile_error()
.into(),
syn::Data::Union(union_data) => Error::new(
union_data.union_token.span,
"[IntoEvent] `derive(IntoEvent)` cannot be applied to Union.",
)
.to_compile_error()
.into(),
}
}
Loading

0 comments on commit 646b24e

Please sign in to comment.