1use serde::Serialize;
2
3use crate::prelude::*;
4
5#[derive(Clone, PartialEq, Eq, Debug, Serialize, Hash)]
16pub struct Event {
17 pub kind: String,
22 pub attributes: Vec<EventAttribute>,
24}
25
26impl Event {
27 pub fn new<K, I>(kind: K, attributes: I) -> Self
45 where
46 K: Into<String>,
47 I: IntoIterator,
48 I::Item: Into<EventAttribute>,
49 {
50 Self {
51 kind: kind.into(),
52 attributes: attributes.into_iter().map(Into::into).collect(),
53 }
54 }
55
56 pub fn eq_ignoring_index(&self, other: &Self) -> bool {
58 self.kind == other.kind
59 && self.attributes.len() == other.attributes.len()
62 && self
63 .attributes
64 .iter()
65 .zip(other.attributes.iter())
66 .all(|(a, b)| a.eq_ignoring_index(b))
67 }
68
69 pub fn hash_ignoring_index<H: core::hash::Hasher>(&self, state: &mut H) {
71 use core::hash::Hash;
72 self.kind.hash(state);
73 state.write_usize(self.attributes.len());
76 for attr in &self.attributes {
77 attr.hash_ignoring_index(state);
78 }
79 }
80}
81
82pub trait TypedEvent
102where
103 Self: TryFrom<Event>,
104 Event: From<Self>,
105{
106 fn into_event(self) -> Event {
108 self.into()
109 }
110}
111
112#[derive(Clone, PartialEq, Eq, Debug, Serialize, Hash)]
119pub struct EventAttribute {
120 pub key: String,
122 pub value: String,
124 pub index: bool,
128}
129
130impl EventAttribute {
131 pub fn eq_ignoring_index(&self, other: &Self) -> bool {
133 self.key == other.key && self.value == other.value
134 }
135
136 pub fn hash_ignoring_index<H: core::hash::Hasher>(&self, state: &mut H) {
138 use core::hash::Hash;
139 (&self.key, &self.value).hash(state);
141 }
142}
143
144impl<K: Into<String>, V: Into<String>> From<(K, V, bool)> for EventAttribute {
145 fn from((key, value, index): (K, V, bool)) -> Self {
146 EventAttribute {
147 key: key.into(),
148 value: value.into(),
149 index,
150 }
151 }
152}
153
154#[allow(missing_docs)]
159pub trait EventAttributeIndexExt: private::Sealed {
160 type Key;
161 type Value;
162
163 fn index(self) -> (Self::Key, Self::Value, bool);
165 fn no_index(self) -> (Self::Key, Self::Value, bool);
167}
168
169impl<K: Into<String>, V: Into<String>> EventAttributeIndexExt for (K, V) {
170 type Key = K;
171 type Value = V;
172 fn index(self) -> (K, V, bool) {
173 let (key, value) = self;
174 (key, value, true)
175 }
176 fn no_index(self) -> (K, V, bool) {
177 let (key, value) = self;
178 (key, value, false)
179 }
180}
181
182mod private {
183 use crate::prelude::*;
184
185 pub trait Sealed {}
186
187 impl<K: Into<String>, V: Into<String>> Sealed for (K, V) {}
188}
189
190impl<K: Into<String>, V: Into<String>> From<(K, V)> for EventAttribute {
191 fn from((key, value): (K, V)) -> Self {
192 (key, value, false).into()
193 }
194}
195
196mod v0_34 {
201 use super::{Event, EventAttribute};
202 use crate::prelude::*;
203 use core::convert::{TryFrom, TryInto};
204
205 use celestia_core_proto::v0_34::abci as pb;
206 use celestia_core_proto::Protobuf;
207
208 impl From<EventAttribute> for pb::EventAttribute {
209 fn from(event: EventAttribute) -> Self {
210 Self {
211 key: event.key.into(),
212 value: event.value.into(),
213 index: event.index,
214 }
215 }
216 }
217
218 impl TryFrom<pb::EventAttribute> for EventAttribute {
219 type Error = crate::Error;
220
221 fn try_from(event: pb::EventAttribute) -> Result<Self, Self::Error> {
222 Ok(Self {
224 key: String::from_utf8(event.key.to_vec())
225 .map_err(|e| crate::Error::parse(e.to_string()))?,
226 value: String::from_utf8(event.value.to_vec())
227 .map_err(|e| crate::Error::parse(e.to_string()))?,
228 index: event.index,
229 })
230 }
231 }
232
233 impl Protobuf<pb::EventAttribute> for EventAttribute {}
234
235 impl From<Event> for pb::Event {
236 fn from(event: Event) -> Self {
237 Self {
238 r#type: event.kind,
239 attributes: event.attributes.into_iter().map(Into::into).collect(),
240 }
241 }
242 }
243
244 impl TryFrom<pb::Event> for Event {
245 type Error = crate::Error;
246
247 fn try_from(event: pb::Event) -> Result<Self, Self::Error> {
248 Ok(Self {
249 kind: event.r#type,
250 attributes: event
251 .attributes
252 .into_iter()
253 .map(TryInto::try_into)
254 .collect::<Result<_, _>>()?,
255 })
256 }
257 }
258
259 impl Protobuf<pb::Event> for Event {}
260}
261
262#[cfg(test)]
263mod tests {
264 #![allow(clippy::bool_assert_comparison)]
265 #![allow(clippy::redundant_clone)]
266
267 use serde::Deserialize;
268
269 use super::*;
270
271 #[test]
272 fn event_eq_ignoring_index_ignores_index() {
273 let event_a = Event::new("test", [("foo", "bar").index()]);
274 let event_b = Event::new("test", [("foo", "bar").no_index()]);
275 let event_c = Event::new("test", [("foo", "baz").index()]);
276
277 assert_eq!(event_a.eq_ignoring_index(&event_b), true);
278 assert_eq!(event_a.eq_ignoring_index(&event_c), false);
279 }
280
281 #[test]
282 fn exercise_typed_event() {
283 #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
284 struct Payload {
285 x: u32,
286 y: u32,
287 }
288
289 #[derive(Clone, Debug, PartialEq, Eq)]
290 struct MyEvent {
291 a: Payload,
292 b: Payload,
293 }
294
295 impl From<MyEvent> for Event {
296 fn from(event: MyEvent) -> Self {
297 Event::new(
298 "my_event",
299 vec![
300 ("a", serde_json::to_string(&event.a).unwrap()).index(),
301 ("b", serde_json::to_string(&event.b).unwrap()).index(),
302 ],
303 )
304 }
305 }
306
307 impl TryFrom<Event> for MyEvent {
308 type Error = (); fn try_from(event: Event) -> Result<Self, Self::Error> {
311 if event.kind != "my_event" {
312 return Err(());
313 }
314
315 let a = event
316 .attributes
317 .iter()
318 .find(|attr| attr.key == "a")
319 .ok_or(())
320 .and_then(|attr| serde_json::from_str(&attr.value).map_err(|_| ()))?;
321 let b = event
322 .attributes
323 .iter()
324 .find(|attr| attr.key == "b")
325 .ok_or(())
326 .and_then(|attr| serde_json::from_str(&attr.value).map_err(|_| ()))?;
327
328 Ok(MyEvent { a, b })
329 }
330 }
331
332 impl TypedEvent for MyEvent {}
333
334 let t = MyEvent {
335 a: Payload { x: 1, y: 2 },
336 b: Payload { x: 3, y: 4 },
337 };
338
339 let e1 = Event::from(t.clone());
340 let e2 = {
342 let mut e = e1.clone();
343 e.attributes[0].index = false;
344 e.attributes[1].index = false;
345 e
346 };
347
348 assert_eq!(e1.eq_ignoring_index(&e2), true);
353 assert_eq!(MyEvent::try_from(e1.clone()), Ok(t.clone()));
354 assert_eq!(MyEvent::try_from(e2.clone()), Ok(t.clone()));
355
356 assert_eq!(
359 Event::from(MyEvent::try_from(e1.clone()).unwrap()).eq_ignoring_index(&e1),
360 true
361 );
362 assert_eq!(
363 Event::from(MyEvent::try_from(e2.clone()).unwrap()).eq_ignoring_index(&e2),
364 true
365 );
366 }
367}