1use crate::{Encode, Output, utils::MaybeUninitBufferWriter};
4use core::marker::PhantomData;
5use gcore::stack_buffer;
6
7pub trait SailsEvent: Encode {
36 fn encoded_event_name(&self) -> &'static [u8];
38
39 fn skip_bytes() -> usize {
43 1
44 }
45}
46
47#[allow(dead_code)]
48fn with_optimized_event_encode<T, E: SailsEvent, F: FnOnce(&[u8]) -> T>(
49 prefix: &[u8],
50 event: E,
51 f: F,
52) -> T {
53 let encoded_event_name = E::encoded_event_name(&event);
54 let encoded_size = Encode::encoded_size(&event);
55 let skip_bytes = E::skip_bytes();
56 let size = prefix.len() + encoded_event_name.len() + encoded_size - skip_bytes;
57 stack_buffer::with_byte_buffer(size, |buffer| {
58 let mut buffer_writer = MaybeUninitBufferWriter::new(buffer);
59 buffer_writer.write(prefix);
60 buffer_writer.write(encoded_event_name);
61 buffer_writer.skip_next(skip_bytes); Encode::encode_to(&event, &mut buffer_writer);
63 buffer_writer.with_buffer(f)
64 })
65}
66
67#[derive(Clone, Debug, Eq, PartialEq)]
71pub struct EventEmitter<T> {
72 route: &'static [u8],
73 _marker: PhantomData<T>,
74}
75
76impl<T> EventEmitter<T> {
77 pub fn new(route: &'static [u8]) -> Self {
78 Self {
79 route,
80 _marker: PhantomData,
81 }
82 }
83}
84
85impl<T: SailsEvent> EventEmitter<T> {
86 #[cfg(target_arch = "wasm32")]
88 pub fn emit_event(&mut self, event: T) -> crate::errors::Result<()> {
89 with_optimized_event_encode(self.route, event, |payload| {
90 gstd::msg::send_bytes(gstd::ActorId::zero(), payload, 0)?;
91 Ok(())
92 })
93 }
94
95 #[cfg(not(target_arch = "wasm32"))]
96 #[cfg(not(feature = "std"))]
97 pub fn emit_event(&mut self, _event: T) -> crate::errors::Result<()> {
98 unimplemented!(
99 "`emit_event` is implemented only for the wasm32 architecture and the std future"
100 )
101 }
102}
103
104#[cfg(feature = "ethexe")]
105impl<T: super::EthEvent> EventEmitter<T> {
106 #[cfg(target_arch = "wasm32")]
108 pub fn emit_eth_event(&mut self, event: T) -> crate::errors::Result<()> {
109 super::ethexe::__emit_eth_event(event)
110 }
111
112 #[cfg(not(target_arch = "wasm32"))]
113 #[cfg(not(feature = "std"))]
114 pub fn emit_eth_event(&mut self, _event: T) -> crate::errors::Result<()> {
115 unimplemented!(
116 "`emit_eth_event` is implemented only for the wasm32 architecture and the std future"
117 )
118 }
119}
120
121#[cfg(not(target_arch = "wasm32"))]
122#[cfg(feature = "std")]
123impl<T: 'static> EventEmitter<T> {
124 pub fn emit_event(&mut self, event: T) -> crate::errors::Result<()> {
126 event_registry::push_event(self.route, event);
127 Ok(())
128 }
129
130 #[cfg(feature = "ethexe")]
131 pub fn emit_eth_event(&mut self, event: T) -> crate::errors::Result<()> {
132 event_registry::push_event(self.route, event);
133 Ok(())
134 }
135
136 pub fn take_events(&mut self) -> crate::Vec<T> {
138 event_registry::take_events(self.route).unwrap_or_else(|| crate::Vec::new())
139 }
140}
141
142#[cfg(not(target_arch = "wasm32"))]
143#[cfg(feature = "std")]
144mod event_registry {
145 use core::any::{Any, TypeId};
146 use std::{boxed::Box, collections::BTreeMap, sync::Mutex, vec::Vec};
147
148 type Key = (&'static [u8], TypeId);
149
150 std::thread_local! {
151 static ROUTE_EVENTS: Mutex<BTreeMap<Key, Box<dyn Any>>> = Mutex::new(BTreeMap::new());
153 }
154
155 pub(super) fn push_event<T: 'static>(key: &'static [u8], value: T) {
158 ROUTE_EVENTS.with(|mtx| {
159 let mut map = mtx.lock().expect("failed to lock ROUTE_EVENTS mutex");
160 let slot = map
161 .entry((key, TypeId::of::<T>()))
162 .or_insert_with(|| Box::new(Vec::<T>::new()));
163 let vec: &mut Vec<T> = slot
166 .downcast_mut::<Vec<T>>()
167 .expect("type mismatch in route-events registry");
168 vec.push(value);
169 });
170 }
171
172 pub(super) fn take_events<T: 'static>(key: &'static [u8]) -> Option<Vec<T>> {
174 ROUTE_EVENTS.with(|mtx| {
175 let mut map = mtx.lock().expect("failed to lock ROUTE_EVENTS mutex");
176 map.remove(&(key, TypeId::of::<T>())).map(|boxed| {
177 *boxed
180 .downcast::<Vec<T>>()
181 .expect("type mismatch in route-events registry")
182 })
183 })
184 }
185
186 #[cfg(test)]
187 mod tests {
188 use super::*;
189 use std::vec;
190
191 #[test]
192 fn event_registry() {
193 push_event(b"/foo", 42_u32);
194 push_event(b"/foo", 7_u32);
195
196 assert_eq!(take_events::<u32>(b"/foo"), Some(vec![42, 7]));
197 assert!(take_events::<u32>(b"/foo").is_none()); assert!(take_events::<i32>(b"/foo").is_none()); }
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206 use crate::prelude::*;
207
208 #[derive(Encode, TypeInfo)]
209 enum TestEvents {
210 Event1(u32),
211 Event2 { p1: u16 },
212 }
213
214 impl SailsEvent for TestEvents {
215 fn encoded_event_name(&self) -> &'static [u8] {
216 match self {
217 TestEvents::Event1(_) => &[24, 69, 118, 101, 110, 116, 49],
218 TestEvents::Event2 { .. } => &[24, 69, 118, 101, 110, 116, 50],
219 }
220 }
221 }
222
223 #[test]
224 fn trait_optimized_event_encode() {
225 let event = TestEvents::Event1(42);
226 with_optimized_event_encode(&[1, 2, 3], event, |payload| {
227 assert_eq!(
228 payload,
229 [1, 2, 3, 24, 69, 118, 101, 110, 116, 49, 42, 00, 00, 00]
230 );
231 });
232
233 let event = TestEvents::Event2 { p1: 43 };
234 with_optimized_event_encode(&[], event, |payload| {
235 assert_eq!(payload, [24, 69, 118, 101, 110, 116, 50, 43, 00]);
236 });
237 }
238}