use serde::{Deserialize, Serialize};
use crate::prelude::*;
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize, Hash)]
pub struct Event {
#[serde(rename = "type", default)]
pub kind: String,
pub attributes: Vec<EventAttribute>,
}
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize, Hash)]
#[serde(untagged)]
pub enum EventAttribute {
V037(v0_37::EventAttribute),
V034(v0_34::EventAttribute),
}
impl EventAttribute {
pub fn key_str(&self) -> Result<&str, crate::Error> {
match self {
EventAttribute::V034(attr) => {
core::str::from_utf8(&attr.key).map_err(|e| crate::Error::parse(e.to_string()))
},
EventAttribute::V037(attr) => Ok(&attr.key),
}
}
pub fn key_bytes(&self) -> &[u8] {
match self {
EventAttribute::V034(attr) => &attr.key,
EventAttribute::V037(attr) => attr.key.as_bytes(),
}
}
pub fn value_str(&self) -> Result<&str, crate::Error> {
match self {
EventAttribute::V034(attr) => {
core::str::from_utf8(&attr.value).map_err(|e| crate::Error::parse(e.to_string()))
},
EventAttribute::V037(attr) => Ok(&attr.value),
}
}
pub fn value_bytes(&self) -> &[u8] {
match self {
EventAttribute::V034(attr) => &attr.value,
EventAttribute::V037(attr) => attr.value.as_bytes(),
}
}
pub fn index(&self) -> bool {
match self {
EventAttribute::V034(attr) => attr.index,
EventAttribute::V037(attr) => attr.index,
}
}
pub fn set_index(&mut self, index: bool) {
match self {
EventAttribute::V034(attr) => attr.index = index,
EventAttribute::V037(attr) => attr.index = index,
}
}
}
impl Event {
pub fn new<K, I>(kind: K, attributes: I) -> Self
where
K: Into<String>,
I: IntoIterator,
I::Item: Into<EventAttribute>,
{
Self {
kind: kind.into(),
attributes: attributes.into_iter().map(Into::into).collect(),
}
}
pub fn eq_ignoring_index(&self, other: &Self) -> bool {
self.kind == other.kind
&& self.attributes.len() == other.attributes.len()
&& self
.attributes
.iter()
.zip(other.attributes.iter())
.all(|(a, b)| a.eq_ignoring_index(b))
}
pub fn hash_ignoring_index<H: core::hash::Hasher>(&self, state: &mut H) {
use core::hash::Hash;
self.kind.hash(state);
state.write_usize(self.attributes.len());
for attr in &self.attributes {
attr.hash_ignoring_index(state);
}
}
}
pub trait TypedEvent
where
Self: TryFrom<Event>,
Event: From<Self>,
{
fn into_event(self) -> Event {
self.into()
}
}
impl EventAttribute {
pub fn eq_ignoring_index(&self, other: &Self) -> bool {
match (self, other) {
(EventAttribute::V034(a), EventAttribute::V034(b)) => {
a.key == b.key && a.value == b.value
},
(EventAttribute::V037(a), EventAttribute::V037(b)) => {
a.key == b.key && a.value == b.value
},
_ => false,
}
}
pub fn hash_ignoring_index<H: core::hash::Hasher>(&self, state: &mut H) {
use core::hash::Hash;
match self {
EventAttribute::V034(attr) => {
(&attr.key, &attr.value).hash(state);
},
EventAttribute::V037(attr) => {
(&attr.key, &attr.value).hash(state);
},
}
}
}
impl<K: Into<String>, V: Into<String>> From<(K, V, bool)> for EventAttribute {
fn from((key, value, index): (K, V, bool)) -> Self {
Self::V037(v0_37::EventAttribute {
key: key.into(),
value: value.into(),
index,
})
}
}
#[allow(missing_docs)]
pub trait EventAttributeIndexExt: private::Sealed {
type Key;
type Value;
fn index(self) -> (Self::Key, Self::Value, bool);
fn no_index(self) -> (Self::Key, Self::Value, bool);
}
impl<K: Into<String>, V: Into<String>> EventAttributeIndexExt for (K, V) {
type Key = K;
type Value = V;
fn index(self) -> (K, V, bool) {
let (key, value) = self;
(key, value, true)
}
fn no_index(self) -> (K, V, bool) {
let (key, value) = self;
(key, value, false)
}
}
mod private {
use crate::prelude::*;
pub trait Sealed {}
impl<K: Into<String>, V: Into<String>> Sealed for (K, V) {}
}
impl<K: Into<String>, V: Into<String>> From<(K, V)> for EventAttribute {
fn from((key, value): (K, V)) -> Self {
(key, value, false).into()
}
}
pub mod v0_34 {
use super::Event;
use crate::prelude::*;
use crate::serializers;
use serde::{Deserialize, Serialize};
use tendermint_proto::v0_34::abci as pb;
use tendermint_proto::Protobuf;
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Hash)]
pub struct EventAttribute {
#[serde(with = "serializers::bytes::base64string")]
pub key: Vec<u8>,
#[serde(with = "serializers::bytes::base64string")]
pub value: Vec<u8>,
pub index: bool,
}
impl From<EventAttribute> for pb::EventAttribute {
fn from(event: EventAttribute) -> Self {
Self {
key: event.key.into(),
value: event.value.into(),
index: event.index,
}
}
}
impl TryFrom<pb::EventAttribute> for EventAttribute {
type Error = crate::Error;
fn try_from(event: pb::EventAttribute) -> Result<Self, Self::Error> {
Ok(Self {
key: event.value.to_vec(),
value: event.value.to_vec(),
index: event.index,
})
}
}
impl Protobuf<pb::EventAttribute> for EventAttribute {}
impl From<Event> for pb::Event {
fn from(event: Event) -> Self {
Self {
r#type: event.kind,
attributes: event
.attributes
.into_iter()
.filter_map(|t| {
let super::EventAttribute::V034(ea) = t else {
return None;
};
Some(ea.into())
})
.collect(),
}
}
}
impl TryFrom<pb::Event> for Event {
type Error = crate::Error;
fn try_from(event: pb::Event) -> Result<Self, Self::Error> {
Ok(Self {
kind: event.r#type,
attributes: event
.attributes
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<EventAttribute>, _>>()?
.into_iter()
.map(super::EventAttribute::V034)
.collect(),
})
}
}
impl Protobuf<pb::Event> for Event {}
}
pub mod v0_37 {
use super::Event;
use crate::prelude::*;
use crate::serializers;
use serde::{Deserialize, Serialize};
use tendermint_proto::v0_37::abci as pb;
use tendermint_proto::Protobuf;
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Hash)]
pub struct EventAttribute {
#[serde(with = "serializers::allow_null")]
pub key: String,
#[serde(with = "serializers::allow_null")]
pub value: String,
pub index: bool,
}
impl From<EventAttribute> for pb::EventAttribute {
fn from(event: EventAttribute) -> Self {
Self {
key: event.key,
value: event.value,
index: event.index,
}
}
}
impl TryFrom<pb::EventAttribute> for EventAttribute {
type Error = crate::Error;
fn try_from(event: pb::EventAttribute) -> Result<Self, Self::Error> {
Ok(Self {
key: event.key,
value: event.value,
index: event.index,
})
}
}
impl Protobuf<pb::EventAttribute> for EventAttribute {}
impl From<Event> for pb::Event {
fn from(event: Event) -> Self {
Self {
r#type: event.kind,
attributes: event
.attributes
.into_iter()
.filter_map(|t| {
let super::EventAttribute::V037(ea) = t else {
return None;
};
Some(ea.into())
})
.collect(),
}
}
}
impl TryFrom<pb::Event> for Event {
type Error = crate::Error;
fn try_from(event: pb::Event) -> Result<Self, Self::Error> {
Ok(Self {
kind: event.r#type,
attributes: event
.attributes
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<EventAttribute>, _>>()?
.into_iter()
.map(super::EventAttribute::V037)
.collect(),
})
}
}
impl Protobuf<pb::Event> for Event {}
}
mod v0_38 {
use super::Event;
use crate::prelude::*;
use tendermint_proto::v0_38::abci as pb;
use tendermint_proto::Protobuf;
pub use super::v0_37::EventAttribute;
impl From<EventAttribute> for pb::EventAttribute {
fn from(event: EventAttribute) -> Self {
Self {
key: event.key,
value: event.value,
index: event.index,
}
}
}
impl TryFrom<pb::EventAttribute> for EventAttribute {
type Error = crate::Error;
fn try_from(event: pb::EventAttribute) -> Result<Self, Self::Error> {
Ok(Self {
key: event.key,
value: event.value,
index: event.index,
})
}
}
impl Protobuf<pb::EventAttribute> for EventAttribute {}
impl From<Event> for pb::Event {
fn from(event: Event) -> Self {
Self {
r#type: event.kind,
attributes: event
.attributes
.into_iter()
.filter_map(|t| {
let super::EventAttribute::V037(ea) = t else {
return None;
};
Some(ea.into())
})
.collect(),
}
}
}
impl TryFrom<pb::Event> for Event {
type Error = crate::Error;
fn try_from(event: pb::Event) -> Result<Self, Self::Error> {
Ok(Self {
kind: event.r#type,
attributes: event
.attributes
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<EventAttribute>, _>>()?
.into_iter()
.map(super::EventAttribute::V037)
.collect(),
})
}
}
impl Protobuf<pb::Event> for Event {}
}
#[cfg(test)]
#[allow(clippy::bool_assert_comparison)]
#[allow(clippy::redundant_clone)]
mod tests {
use serde::Deserialize;
use super::*;
#[test]
fn event_eq_ignoring_index_ignores_index() {
let event_a = Event::new("test", [("foo", "bar").index()]);
let event_b = Event::new("test", [("foo", "bar").no_index()]);
let event_c = Event::new("test", [("foo", "baz").index()]);
assert_eq!(event_a.eq_ignoring_index(&event_b), true);
assert_eq!(event_a.eq_ignoring_index(&event_c), false);
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Payload {
x: u32,
y: u32,
}
#[derive(Clone, Debug, PartialEq, Eq)]
struct MyEvent {
a: Payload,
b: Payload,
}
impl From<MyEvent> for Event {
fn from(event: MyEvent) -> Self {
Event::new(
"my_event",
vec![
("a", serde_json::to_string(&event.a).unwrap()).index(),
("b", serde_json::to_string(&event.b).unwrap()).index(),
],
)
}
}
impl TryFrom<Event> for MyEvent {
type Error = ();
fn try_from(event: Event) -> Result<Self, Self::Error> {
if event.kind != "my_event" {
return Err(());
}
let a = event
.attributes
.iter()
.find(|attr| attr.key_bytes() == b"a")
.ok_or(())
.and_then(|attr| serde_json::from_slice(attr.value_bytes()).map_err(|_| ()))?;
let b = event
.attributes
.iter()
.find(|attr| attr.key_bytes() == b"b")
.ok_or(())
.and_then(|attr| serde_json::from_slice(attr.value_bytes()).map_err(|_| ()))?;
Ok(MyEvent { a, b })
}
}
impl TypedEvent for MyEvent {}
#[test]
fn exercise_typed_event() {
let t = MyEvent {
a: Payload { x: 1, y: 2 },
b: Payload { x: 3, y: 4 },
};
let e1 = Event::from(t.clone());
let e2 = {
let mut e = e1.clone();
e.attributes[0].set_index(false);
e.attributes[1].set_index(false);
e
};
assert_eq!(e1.eq_ignoring_index(&e2), true);
assert_eq!(MyEvent::try_from(e1.clone()), Ok(t.clone()));
assert_eq!(MyEvent::try_from(e2.clone()), Ok(t.clone()));
assert_eq!(
Event::from(MyEvent::try_from(e1.clone()).unwrap()).eq_ignoring_index(&e1),
true
);
assert_eq!(
Event::from(MyEvent::try_from(e2.clone()).unwrap()).eq_ignoring_index(&e2),
true
);
}
}