286 lines
9.0 KiB
Rust
286 lines
9.0 KiB
Rust
//! This crate provides a mechanism to convert arbitrary rust structs into `cosmwasm_std::Event`s
|
|
//! via that struct's `Serialize` impl.
|
|
|
|
use cosmwasm_std::Event;
|
|
use serde::Serialize;
|
|
|
|
mod error;
|
|
mod ser;
|
|
|
|
pub use error::Error;
|
|
use ser::Serializer;
|
|
|
|
/// Convert `value` into a `cosmwasm_std::Event` via its `Serialize` impl.
|
|
///
|
|
/// `value` must serialize as exactly one regular struct, a unit struct, a struct variant of an
|
|
/// enum, or an `Option` that contains a value that serializes as one of of the previous 3 types.
|
|
/// Attempting to convert any other type (including `Option::None`) to an `Event` will return
|
|
/// `Error::NotAStruct`.
|
|
///
|
|
/// The name of the struct will become the type of the returned event while the fields of the struct
|
|
/// will become the event attributes. In the case of a struct variant of an enum, the type of the
|
|
/// event will be "{name}::{variant}". Field values are encoded using the `serde-json-wasm` crate
|
|
/// and field types may be any type supported by that crate.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # fn example() -> Result<(), cw_transcode::Error> {
|
|
/// # use cosmwasm_std::{Binary, Event};
|
|
/// # use serde::Serialize;
|
|
/// #
|
|
/// # #[derive(Serialize)]
|
|
/// # struct Nested {
|
|
/// # f1: u32,
|
|
/// # f2: String,
|
|
/// # }
|
|
/// #
|
|
/// # #[derive(Serialize)]
|
|
/// # struct Example {
|
|
/// # primitive: i64,
|
|
/// # nested: Nested,
|
|
/// # binary: Binary,
|
|
/// # }
|
|
/// #
|
|
/// use cw_transcode::to_event;
|
|
///
|
|
/// let e = Example {
|
|
/// // Primitive values are supported.
|
|
/// primitive: 0x9a82f2b2865c2fdau64 as i64,
|
|
///
|
|
/// // Nested struct fields are encoded as json objects.
|
|
/// nested: Nested {
|
|
/// f1: 0xdbc7b8a1u32,
|
|
/// f2: "TEST".to_string(),
|
|
/// },
|
|
///
|
|
/// // Other types supported by `serde-json-wasm` are also fine.
|
|
/// binary: Binary(vec![
|
|
/// 0xdb, 0x78, 0x97, 0x0e, 0xf2, 0x55, 0x97, 0xf4, 0x66, 0x11, 0x91, 0x0f, 0x50, 0x57,
|
|
/// 0xb8, 0xe7,
|
|
/// ]),
|
|
/// };
|
|
///
|
|
/// let evt = to_event(&e)?;
|
|
///
|
|
/// let expected = Event::new("Example")
|
|
/// .add_attribute("primitive", "-7313015996323975206")
|
|
/// .add_attribute("nested", r#"{"f1":3687299233,"f2":"TEST"}"#)
|
|
/// .add_attribute("binary", "\"23iXDvJVl/RmEZEPUFe45w==\"");
|
|
///
|
|
/// assert_eq!(expected, evt);
|
|
/// #
|
|
/// # Ok(())
|
|
/// # }
|
|
/// #
|
|
/// # example().unwrap();
|
|
/// ```
|
|
pub fn to_event<T: Serialize + ?Sized>(value: &T) -> Result<Event, Error> {
|
|
let mut s = Serializer::new();
|
|
value.serialize(&mut s)?;
|
|
s.finish().ok_or(Error::NoEvent)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
use std::{any::type_name, collections::BTreeMap};
|
|
|
|
use cosmwasm_std::Binary;
|
|
use serde_bytes::ByteBuf;
|
|
|
|
#[test]
|
|
fn unsupported() {
|
|
macro_rules! test_unsupported {
|
|
($($ty:ty),* $(,)?) => {
|
|
$(
|
|
let x: $ty = Default::default();
|
|
let name = type_name::<$ty>();
|
|
let err = to_event(&x).expect_err(name);
|
|
assert!(matches!(err, Error::NotAStruct), "{name}");
|
|
)*
|
|
}
|
|
}
|
|
|
|
test_unsupported!(
|
|
i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, f32, f64, char,
|
|
String, Vec<u8>, ByteBuf, BTreeMap<u32, u32>, Option<u64>, (), (u32, f32, i64)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn basic() {
|
|
#[derive(Serialize)]
|
|
struct Transfer {
|
|
tx_hash: Binary,
|
|
timestamp: u32,
|
|
nonce: u32,
|
|
emitter_chain: u16,
|
|
#[serde(with = "hex")]
|
|
emitter_address: [u8; 32],
|
|
sequence: u64,
|
|
consistency_level: u8,
|
|
payload: Binary,
|
|
}
|
|
|
|
let tx = Transfer {
|
|
tx_hash: Binary(vec![
|
|
0x82, 0xea, 0x25, 0x36, 0xc5, 0xd1, 0x67, 0x18, 0x30, 0xcb, 0x49, 0x12, 0x0f, 0x94,
|
|
0x47, 0x9e, 0x34, 0xb5, 0x45, 0x96, 0xa8, 0xdd, 0x36, 0x9f, 0xbc, 0x26, 0x66, 0x66,
|
|
0x7a, 0x76, 0x5f, 0x4b,
|
|
]),
|
|
timestamp: 1672860466,
|
|
nonce: 0,
|
|
emitter_chain: 2,
|
|
emitter_address: [
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x90,
|
|
0xfb, 0x16, 0x72, 0x08, 0xaf, 0x45, 0x5b, 0xb1, 0x37, 0x78, 0x01, 0x63, 0xb7, 0xb7,
|
|
0xa9, 0xa1, 0x0c, 0x16,
|
|
],
|
|
sequence: 1672860466,
|
|
consistency_level: 15,
|
|
payload: Binary(vec![
|
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xe0, 0xb6,
|
|
0xb3, 0xa7, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x2d, 0x8b, 0xe6, 0xbf, 0x0b, 0xaa, 0x74, 0xe0, 0xa9, 0x07, 0x01,
|
|
0x66, 0x79, 0xca, 0xe9, 0x19, 0x0e, 0x80, 0xdd, 0x0a, 0x00, 0x02, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x08, 0x20, 0x98, 0x3f,
|
|
0x33, 0x45, 0x6c, 0xe7, 0xbe, 0xb3, 0xa0, 0x46, 0xf5, 0xa8, 0x3f, 0xa3, 0x4f, 0x02,
|
|
0x7d, 0x0c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
]),
|
|
};
|
|
|
|
let expected = Event::new("Transfer")
|
|
.add_attribute(
|
|
"tx_hash",
|
|
"\"guolNsXRZxgwy0kSD5RHnjS1RZao3TafvCZmZnp2X0s=\"",
|
|
)
|
|
.add_attribute("timestamp", "1672860466")
|
|
.add_attribute("nonce", "0")
|
|
.add_attribute("emitter_chain", "2")
|
|
.add_attribute(
|
|
"emitter_address",
|
|
"\"0000000000000000000000000290fb167208af455bb137780163b7b7a9a10c16\"",
|
|
)
|
|
.add_attribute("sequence", "1672860466")
|
|
.add_attribute("consistency_level", "15")
|
|
.add_attribute("payload", "\"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3gtrOnZAAAAAAAAAAAAAAAAAAALYvmvwuqdOCpBwFmecrpGQ6A3QoAAgAAAAAAAAAAAAAAAMEIIJg/M0Vs576zoEb1qD+jTwJ9DCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==\"");
|
|
|
|
let actual = to_event(&tx).unwrap();
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn option() {
|
|
#[derive(Serialize)]
|
|
struct Data {
|
|
a: u32,
|
|
b: String,
|
|
}
|
|
|
|
let d = Some(Data {
|
|
a: 17,
|
|
b: "BEEF".into(),
|
|
});
|
|
|
|
let expected = Event::new("Data")
|
|
.add_attribute("a", "17")
|
|
.add_attribute("b", "\"BEEF\"");
|
|
|
|
let actual = to_event(&d).unwrap();
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn unit_struct() {
|
|
#[derive(Serialize)]
|
|
struct MyEvent;
|
|
|
|
let expected = Event::new("MyEvent");
|
|
let actual = to_event(&MyEvent).unwrap();
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn enum_variants() {
|
|
#[derive(Serialize)]
|
|
enum MyEvent {
|
|
A,
|
|
B(u32),
|
|
C { f1: u64, f2: String },
|
|
D(u16, u16, u32),
|
|
}
|
|
|
|
// Unit variants.
|
|
{
|
|
let expected = Event::new("MyEvent::A");
|
|
let actual = to_event(&MyEvent::A).unwrap();
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
// Newtype variants.
|
|
{
|
|
let err = to_event(&MyEvent::B(19)).unwrap_err();
|
|
assert!(matches!(err, Error::NotAStruct));
|
|
}
|
|
|
|
// Struct variants.
|
|
{
|
|
let c = MyEvent::C {
|
|
f1: 500,
|
|
f2: "test struct variant".into(),
|
|
};
|
|
let expected = Event::new("MyEvent::C")
|
|
.add_attribute("f1", "500")
|
|
.add_attribute("f2", "\"test struct variant\"");
|
|
let actual = to_event(&c).unwrap();
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
// Tuple variants.
|
|
{
|
|
let err = to_event(&MyEvent::D(37, 1926, 189174)).unwrap_err();
|
|
assert!(matches!(err, Error::NotAStruct));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn nested() {
|
|
#[derive(Serialize)]
|
|
struct Nested {
|
|
a: u32,
|
|
b: Binary,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct Outer {
|
|
nested: Nested,
|
|
c: String,
|
|
}
|
|
|
|
let o = Outer {
|
|
nested: Nested {
|
|
a: 0xfeb42045,
|
|
b: Binary(vec![
|
|
0x11, 0x7d, 0x83, 0xa4, 0x06, 0xbf, 0x3e, 0x50, 0xe1, 0xaa, 0x19, 0x89, 0xc3,
|
|
0x00, 0xea, 0xaf,
|
|
]),
|
|
},
|
|
c: "TEST".into(),
|
|
};
|
|
|
|
let expected = Event::new("Outer")
|
|
.add_attribute(
|
|
"nested",
|
|
r#"{"a":4273217605,"b":"EX2DpAa/PlDhqhmJwwDqrw=="}"#,
|
|
)
|
|
.add_attribute("c", "\"TEST\"");
|
|
let actual = to_event(&o).unwrap();
|
|
assert_eq!(expected, actual);
|
|
}
|
|
}
|