use crate::{Encode, Output, utils::MaybeUninitBufferWriter};
use core::marker::PhantomData;
use gcore::stack_buffer;
use sails_idl_meta::InterfaceId;
pub trait SailsEvent: Encode {
fn encoded_event_name(&self) -> &'static [u8];
fn entry_id(&self) -> u16;
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_size = Encode::encoded_size(&event);
let skip_bytes = E::skip_bytes();
let size = prefix.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.skip_next(skip_bytes); Encode::encode_to(&event, &mut buffer_writer);
buffer_writer.with_buffer(f)
})
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct EventEmitter<E> {
interface_id: InterfaceId,
route_idx: u8,
_marker: PhantomData<E>,
}
impl<E> EventEmitter<E> {
pub fn new(interface_id: InterfaceId, route_idx: u8) -> Self {
Self {
interface_id,
route_idx,
_marker: PhantomData,
}
}
}
impl<E: SailsEvent> EventEmitter<E> {
#[cfg(target_arch = "wasm32")]
pub fn emit_event(&mut self, event: E) -> crate::errors::Result<()> {
let header = crate::meta::SailsMessageHeader::v1(
self.interface_id,
event.entry_id(),
self.route_idx,
);
with_optimized_event_encode(header.to_bytes().as_slice(), 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: E) -> 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_idx, event);
Ok(())
}
#[cfg(feature = "ethexe")]
pub fn emit_eth_event(&mut self, event: T) -> crate::errors::Result<()> {
event_registry::push_event(self.route_idx, event);
Ok(())
}
pub fn take_events(&mut self) -> crate::Vec<T> {
event_registry::take_events(self.route_idx).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 = (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: 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: 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(1, 42_u32);
push_event(1, 7_u32);
assert_eq!(take_events::<u32>(1), Some(vec![42, 7]));
assert!(take_events::<u32>(1).is_none()); assert!(take_events::<i32>(1).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],
}
}
fn entry_id(&self) -> u16 {
match self {
TestEvents::Event1(_) => 0,
TestEvents::Event2 { .. } => 1,
}
}
}
#[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, 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, [43, 00]);
});
}
}