use crate::{Encode, Output, utils::MaybeUninitBufferWriter};
use core::marker::PhantomData;
use gcore::stack_buffer;
pub trait SailsEvent: Encode {
fn encoded_event_name(&self) -> &'static [u8];
fn skip_bytes() -> usize {
1
}
}
#[allow(dead_code)]
fn with_optimized_event_encode<T, E: SailsEvent, F: FnOnce(&[u8]) -> T>(
prefix: &[u8],
event: E,
f: F,
) -> T {
let encoded_event_name = E::encoded_event_name(&event);
let encoded_size = Encode::encoded_size(&event);
let skip_bytes = E::skip_bytes();
let size = prefix.len() + encoded_event_name.len() + encoded_size - skip_bytes;
stack_buffer::with_byte_buffer(size, |buffer| {
let mut buffer_writer = MaybeUninitBufferWriter::new(buffer);
buffer_writer.write(prefix);
buffer_writer.write(encoded_event_name);
buffer_writer.skip_next(skip_bytes); Encode::encode_to(&event, &mut buffer_writer);
buffer_writer.with_buffer(f)
})
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct EventEmitter<T> {
route: &'static [u8],
_marker: PhantomData<T>,
}
impl<T> EventEmitter<T> {
pub fn new(route: &'static [u8]) -> Self {
Self {
route,
_marker: PhantomData,
}
}
}
impl<T: SailsEvent> EventEmitter<T> {
#[cfg(target_arch = "wasm32")]
pub fn emit_event(&mut self, event: T) -> crate::errors::Result<()> {
with_optimized_event_encode(self.route, event, |payload| {
gstd::msg::send_bytes(gstd::ActorId::zero(), payload, 0)?;
Ok(())
})
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(feature = "std"))]
pub fn emit_event(&mut self, _event: T) -> crate::errors::Result<()> {
unimplemented!(
"`emit_event` is implemented only for the wasm32 architecture and the std future"
)
}
}
#[cfg(feature = "ethexe")]
impl<T: super::EthEvent> EventEmitter<T> {
#[cfg(target_arch = "wasm32")]
pub fn emit_eth_event(&mut self, event: T) -> crate::errors::Result<()> {
super::ethexe::__emit_eth_event(event)
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(feature = "std"))]
pub fn emit_eth_event(&mut self, _event: T) -> crate::errors::Result<()> {
unimplemented!(
"`emit_eth_event` is implemented only for the wasm32 architecture and the std future"
)
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "std")]
impl<T: 'static> EventEmitter<T> {
pub fn emit_event(&mut self, event: T) -> crate::errors::Result<()> {
event_registry::push_event(self.route, event);
Ok(())
}
#[cfg(feature = "ethexe")]
pub fn emit_eth_event(&mut self, event: T) -> crate::errors::Result<()> {
event_registry::push_event(self.route, event);
Ok(())
}
pub fn take_events(&mut self) -> crate::Vec<T> {
event_registry::take_events(self.route).unwrap_or_else(|| crate::Vec::new())
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "std")]
mod event_registry {
use core::any::{Any, TypeId};
use std::{boxed::Box, collections::BTreeMap, sync::Mutex, vec::Vec};
type Key = (&'static [u8], TypeId);
std::thread_local! {
static ROUTE_EVENTS: Mutex<BTreeMap<Key, Box<dyn Any>>> = Mutex::new(BTreeMap::new());
}
pub(super) fn push_event<T: 'static>(key: &'static [u8], value: T) {
ROUTE_EVENTS.with(|mtx| {
let mut map = mtx.lock().expect("failed to lock ROUTE_EVENTS mutex");
let slot = map
.entry((key, TypeId::of::<T>()))
.or_insert_with(|| Box::new(Vec::<T>::new()));
let vec: &mut Vec<T> = slot
.downcast_mut::<Vec<T>>()
.expect("type mismatch in route-events registry");
vec.push(value);
});
}
pub(super) fn take_events<T: 'static>(key: &'static [u8]) -> Option<Vec<T>> {
ROUTE_EVENTS.with(|mtx| {
let mut map = mtx.lock().expect("failed to lock ROUTE_EVENTS mutex");
map.remove(&(key, TypeId::of::<T>())).map(|boxed| {
*boxed
.downcast::<Vec<T>>()
.expect("type mismatch in route-events registry")
})
})
}
#[cfg(test)]
mod tests {
use super::*;
use std::vec;
#[test]
fn event_registry() {
push_event(b"/foo", 42_u32);
push_event(b"/foo", 7_u32);
assert_eq!(take_events::<u32>(b"/foo"), Some(vec![42, 7]));
assert!(take_events::<u32>(b"/foo").is_none()); assert!(take_events::<i32>(b"/foo").is_none()); }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
#[derive(Encode, TypeInfo)]
enum TestEvents {
Event1(u32),
Event2 { p1: u16 },
}
impl SailsEvent for TestEvents {
fn encoded_event_name(&self) -> &'static [u8] {
match self {
TestEvents::Event1(_) => &[24, 69, 118, 101, 110, 116, 49],
TestEvents::Event2 { .. } => &[24, 69, 118, 101, 110, 116, 50],
}
}
}
#[test]
fn trait_optimized_event_encode() {
let event = TestEvents::Event1(42);
assert_eq!(event.encode(), &[0, 42, 0, 0, 0]);
with_optimized_event_encode(&[1, 2, 3], event, |payload| {
assert_eq!(
payload,
[1, 2, 3, 24, 69, 118, 101, 110, 116, 49, 42, 00, 00, 00]
);
});
let event = TestEvents::Event2 { p1: 43 };
assert_eq!(event.encode(), &[1, 43, 0]);
with_optimized_event_encode(&[], event, |payload| {
assert_eq!(payload, [24, 69, 118, 101, 110, 116, 50, 43, 00]);
});
}
}