1#![doc(html_favicon_url = "https://dev.namada.net/master/favicon.png")]
4#![doc(html_logo_url = "https://dev.namada.net/master/rustdoc-logo.png")]
5#![deny(rustdoc::broken_intra_doc_links)]
6#![deny(rustdoc::private_intra_doc_links)]
7#![warn(
8 missing_docs,
9 rust_2018_idioms,
10 clippy::cast_sign_loss,
11 clippy::cast_possible_truncation,
12 clippy::cast_possible_wrap,
13 clippy::cast_lossless,
14 clippy::arithmetic_side_effects,
15 clippy::dbg_macro,
16 clippy::print_stdout,
17 clippy::print_stderr
18)]
19
20pub mod extend;
21#[cfg(any(test, feature = "testing"))]
22pub mod testing;
23#[cfg(any(test, feature = "debug"))]
24pub mod tracer;
25
26use std::borrow::Cow;
27use std::collections::BTreeMap;
28use std::fmt::{self, Display};
29use std::ops::Deref;
30use std::str::FromStr;
31
32use namada_core::borsh::{BorshDeserialize, BorshSerialize};
33use namada_macros::BorshDeserializer;
34#[cfg(feature = "migrations")]
35use namada_migrations::*;
36use serde::{Deserialize, Serialize};
37use thiserror::Error;
38
39#[doc(hidden)]
40#[macro_export]
41macro_rules! __event_type_impl {
42 ($domain:ty) => {
43 <$domain as $crate::EventToEmit>::DOMAIN
44 };
45 ($domain:ty, $($subdomain:expr),*) => {
46 ::konst::string::str_join!(
47 "/",
48 &[
49 $crate::__event_type_impl!($domain),
50 $($subdomain),*
51 ],
52 )
53 };
54}
55
56#[macro_export]
65macro_rules! event_type {
66 ($($tt:tt)*) => {
67 $crate::EventType::new($crate::__event_type_impl!($($tt)*))
68 };
69}
70
71pub trait EventToEmit: Into<Event> {
73 const DOMAIN: &'static str;
77}
78
79impl EventToEmit for Event {
80 const DOMAIN: &'static str = "unknown";
81}
82
83pub trait EmitEvents {
85 fn emit<E>(&mut self, event: E)
87 where
88 E: EventToEmit;
89
90 fn emit_many<B, E>(&mut self, event_batch: B)
92 where
93 B: IntoIterator<Item = E>,
94 E: EventToEmit;
95}
96
97impl EmitEvents for Vec<Event> {
98 #[inline]
99 fn emit<E>(&mut self, event: E)
100 where
101 E: Into<Event>,
102 {
103 self.push(event.into());
104 }
105
106 fn emit_many<B, E>(&mut self, event_batch: B)
108 where
109 B: IntoIterator<Item = E>,
110 E: Into<Event>,
111 {
112 self.extend(event_batch.into_iter().map(Into::into));
113 }
114}
115
116#[derive(
119 Clone,
120 Debug,
121 Eq,
122 PartialEq,
123 Ord,
124 PartialOrd,
125 Hash,
126 BorshSerialize,
127 BorshDeserialize,
128 BorshDeserializer,
129 Serialize,
130 Deserialize,
131)]
132pub enum EventLevel {
133 Block,
135 Tx,
137}
138
139impl Display for EventLevel {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 write!(
142 f,
143 "{}",
144 match self {
145 EventLevel::Block => "block",
146 EventLevel::Tx => "tx",
147 }
148 )
149 }
150}
151
152#[derive(
157 Clone,
158 Debug,
159 Eq,
160 PartialEq,
161 Ord,
162 PartialOrd,
163 Hash,
164 BorshSerialize,
165 BorshDeserialize,
166 BorshDeserializer,
167 Serialize,
168 Deserialize,
169)]
170#[repr(transparent)]
171pub struct EventType {
172 inner: Cow<'static, str>,
173}
174
175impl Deref for EventType {
176 type Target = str;
177
178 #[inline(always)]
179 fn deref(&self) -> &str {
180 &self.inner
181 }
182}
183
184impl EventType {
185 pub const fn new(event_type: &'static str) -> Self {
187 Self {
188 inner: Cow::Borrowed(event_type),
189 }
190 }
191
192 #[inline]
194 pub fn domain(&self) -> &str {
195 self.inner
196 .split_once('/')
197 .map(|(domain, _sub_domain)| domain)
198 .unwrap_or("unknown")
199 }
200
201 #[inline]
203 pub fn sub_domain(&self) -> &str {
204 self.inner
205 .split_once('/')
206 .map(|(_domain, sub_domain)| sub_domain)
207 .unwrap_or("")
208 }
209}
210
211impl Display for EventType {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 write!(f, "{}", self.inner)
214 }
215}
216
217impl FromStr for EventType {
218 type Err = EventError;
219
220 fn from_str(s: &str) -> Result<Self, Self::Err> {
221 s.split_once('/').ok_or(EventError::MissingDomain)?;
222 Ok(Self {
223 inner: Cow::Owned(s.into()),
224 })
225 }
226}
227
228pub struct EventTypeBuilder {
230 inner: String,
231}
232
233impl EventTypeBuilder {
234 #[inline]
236 pub fn new_with_type(ty: impl Into<String>) -> Self {
237 Self { inner: ty.into() }
238 }
239
240 #[inline]
243 pub fn new_of<E: EventToEmit>() -> Self {
244 Self::new_with_type(E::DOMAIN)
245 }
246
247 #[inline]
250 pub fn append_segment(&mut self, segment: impl AsRef<str>) -> &mut Self {
251 let segment = segment.as_ref();
252
253 if !segment.is_empty() {
254 self.inner.push('/');
255 self.inner.push_str(segment.as_ref());
256 }
257
258 self
259 }
260
261 #[inline]
264 pub fn with_segment(mut self, segment: impl AsRef<str>) -> Self {
265 self.append_segment(segment);
266 self
267 }
268
269 #[inline]
271 pub fn build(self) -> EventType {
272 EventType {
273 inner: Cow::Owned(self.inner),
274 }
275 }
276}
277
278#[derive(
281 Clone,
282 Debug,
283 Eq,
284 PartialEq,
285 Ord,
286 PartialOrd,
287 Hash,
288 BorshSerialize,
289 BorshDeserialize,
290 BorshDeserializer,
291 Serialize,
292 Deserialize,
293)]
294pub struct Event {
295 level: EventLevel,
298 event_type: EventType,
300 attributes: BTreeMap<String, String>,
302}
303
304#[derive(Error, Debug, Clone)]
306pub enum EventError {
307 #[error("Invalid event domain: {0}")]
309 InvalidDomain(String),
310 #[error("Missing the domain of the event")]
312 MissingDomain,
313 #[error("Missing event attribute {0:?}")]
315 MissingAttribute(&'static str),
316 #[error("Failed to parse event attribute: {0}")]
318 AttributeEncoding(String),
319 #[error("Invalid event type")]
321 InvalidEventType,
322 #[error("Json missing `attributes` field")]
324 MissingAttributes,
325 #[error("Attributes missing key: {0}")]
327 MissingKey(String),
328 #[error("Attributes missing value: {0}")]
330 MissingValue(String),
331}
332
333impl Event {
334 pub fn new(event_type: EventType, level: EventLevel) -> Self {
336 Self {
337 event_type,
338 level,
339 attributes: BTreeMap::new(),
340 }
341 }
342
343 #[inline]
345 pub fn level(&self) -> &EventLevel {
346 &self.level
347 }
348
349 #[inline]
351 pub fn kind(&self) -> &EventType {
352 &self.event_type
353 }
354
355 #[deprecated = "Accessing the event attributes directly is deprecated. \
357 Consider using domain types to compose events with \
358 attributes."]
359 #[inline]
360 pub fn attributes(&self) -> &BTreeMap<String, String> {
361 &self.attributes
362 }
363
364 #[deprecated = "Accessing the event attributes directly is deprecated. \
366 Consider using domain types to compose events with \
367 attributes."]
368 #[inline]
369 pub fn attributes_mut(&mut self) -> &mut BTreeMap<String, String> {
370 &mut self.attributes
371 }
372
373 #[inline]
376 pub fn into_attributes(self) -> BTreeMap<String, String> {
377 self.attributes
378 }
379
380 #[inline]
383 pub fn has_subset_of_attrs<A: extend::AttributesMap>(
384 &self,
385 attrs: &A,
386 ) -> bool {
387 attrs.iter_attributes().all(|(key, value)| {
388 match self.attributes.get(key) {
389 Some(v) => v == value,
390 None => false,
391 }
392 })
393 }
394
395 #[inline]
397 pub fn delete_attribute<DATA>(&mut self)
398 where
399 DATA: extend::DeleteFromEventAttributes,
400 {
401 DATA::delete_from_event_attributes(&mut self.attributes);
402 }
403
404 #[inline]
407 pub fn raw_read_attribute<'value, DATA>(&self) -> Option<&str>
408 where
409 DATA: extend::RawReadFromEventAttributes<'value>,
410 {
411 DATA::raw_read_opt_from_event_attributes(&self.attributes)
412 }
413
414 #[inline]
416 pub fn read_attribute<'value, DATA>(
417 &self,
418 ) -> Result<
419 <DATA as extend::ReadFromEventAttributes<'value>>::Value,
420 EventError,
421 >
422 where
423 DATA: extend::ReadFromEventAttributes<'value>,
424 {
425 DATA::read_from_event_attributes(&self.attributes)
426 }
427
428 #[inline]
430 pub fn read_attribute_opt<'value, DATA>(
431 &self,
432 ) -> Result<
433 Option<<DATA as extend::ReadFromEventAttributes<'value>>::Value>,
434 EventError,
435 >
436 where
437 DATA: extend::ReadFromEventAttributes<'value>,
438 {
439 DATA::read_opt_from_event_attributes(&self.attributes)
440 }
441
442 #[inline]
444 pub fn has_attribute<'value, DATA>(&self) -> bool
445 where
446 DATA: extend::RawReadFromEventAttributes<'value>,
447 {
448 DATA::check_if_present_in(&self.attributes)
449 }
450
451 #[inline]
453 pub fn extend<DATA>(&mut self, data: DATA) -> &mut Self
454 where
455 DATA: extend::ExtendEvent,
456 {
457 data.extend_event(self);
458 self
459 }
460
461 #[inline]
464 pub fn emission_gas_cost(&self, cost_per_byte: u64) -> Option<u64> {
465 let len = self.attributes.iter().try_fold(0_usize, |acc, (k, v)| {
466 acc.checked_add(k.len())
467 .and_then(|val| val.checked_add(v.len()))
468 })?;
469 (len as u64).checked_mul(cost_per_byte)
470 }
471
472 pub fn merge(&mut self, other: Self) {
474 self.attributes.extend(other.attributes)
475 }
476}
477
478impl From<Event> for namada_core::tendermint_proto::abci::Event {
479 fn from(event: Event) -> Self {
480 Self {
481 r#type: {
482 use extend::{Domain, RawReadFromEventAttributes};
483
484 if Domain::<Event>::check_if_present_in(&event.attributes) {
485 event.event_type.sub_domain().to_string()
489 } else {
490 event.event_type.to_string()
491 }
492 },
493 attributes: event
494 .attributes
495 .into_iter()
496 .map(|(key, value)| {
497 namada_core::tendermint_proto::abci::EventAttribute {
498 key,
499 value,
500 index: true,
501 }
502 })
503 .chain(std::iter::once_with(|| {
504 namada_core::tendermint_proto::abci::EventAttribute {
505 key: "event-level".to_string(),
506 value: event.level.to_string(),
507 index: true,
508 }
509 }))
510 .collect(),
511 }
512 }
513}
514
515impl From<Event> for namada_core::tendermint::abci::Event {
516 fn from(event: Event) -> Self {
517 Self {
518 kind: {
519 use extend::{Domain, RawReadFromEventAttributes};
520
521 if Domain::<Event>::check_if_present_in(&event.attributes) {
522 event.event_type.sub_domain().to_string()
526 } else {
527 event.event_type.to_string()
528 }
529 },
530 attributes: event
531 .attributes
532 .into_iter()
533 .map(|(key, value)| (key, value, true).into())
534 .chain(std::iter::once_with(|| {
535 ("event-level", event.level.to_string(), true).into()
536 }))
537 .collect(),
538 }
539 }
540}