1use crate::call::with_scratch;
2use crate::exn::*;
3use crate::internal::encode_scratch;
4use crate::re_throw::OrSyscallThrow;
5use crate::sys::{self, SyscallNo};
6
7pub use thruput_event::{EmittedEvent, Event, Topic, TopicSrc, TypedEvent};
9
10#[diagnostic::on_unimplemented(
19 message = "`{Self}` cannot be an event topic",
20 note = "either widen the field to a listed type, or drop `#[topic]` and let it ride in the event payload, where it stays readable"
23)]
24pub trait IntoTopic {
25 #[allow(clippy::wrong_self_convention)]
26 fn into_topic(&self) -> TopicSrc<'_>;
27}
28
29impl IntoTopic for [u8; 32] {
30 fn into_topic(&self) -> TopicSrc<'_> {
31 TopicSrc::Shared(self)
32 }
33}
34
35pub const EVENT_MAX_TOPICS: u32 = thruput_xdr::event::kMaxTopics;
37
38pub const EVENT_MAX_DATA: u32 = thruput_xdr::event::kMaxEventLength;
40
41pub fn hash_to_topic<T: crate::WriteXDR>(val: &T) -> Topic {
43 with_scratch(|buf| {
44 encode_scratch(buf, val).unwrap_or_else(|e| encoding_event_throw(e));
45 crate::crypto::sha256(buf.as_slice()).into_inner()
46 })
47}
48
49macro_rules! impl_into_topic_direct {
51 ($wide:ty; $($ty:ty),*) => {
52 $(
53 impl IntoTopic for $ty {
54 fn into_topic(&self) -> TopicSrc<'_> {
55 let value: $wide = (*self).into();
56 TopicSrc::Owned(value.to_be_bytes())
57 }
58 }
59 )*
60 };
61}
62
63impl_into_topic_direct!(crate::U256; u128, u64, u32, bool);
64impl_into_topic_direct!(crate::I256; i128, i64, i32);
65
66impl IntoTopic for str {
67 fn into_topic(&self) -> TopicSrc<'_> {
68 TopicSrc::Owned(hash_to_topic(&self))
69 }
70}
71
72impl IntoTopic for [u8] {
73 fn into_topic(&self) -> TopicSrc<'_> {
74 TopicSrc::Owned(hash_to_topic(&self))
75 }
76}
77
78impl IntoTopic for crate::types::ContractAddress {
83 fn into_topic(&self) -> TopicSrc<'_> {
84 (**self).into_topic()
85 }
86}
87
88impl IntoTopic for crate::types::SHA256Hash {
89 fn into_topic(&self) -> TopicSrc<'_> {
90 (**self).into_topic()
91 }
92}
93
94impl IntoTopic for crate::U256 {
95 fn into_topic(&self) -> TopicSrc<'_> {
96 TopicSrc::Owned(self.to_be_bytes())
97 }
98}
99
100impl IntoTopic for crate::I256 {
101 fn into_topic(&self) -> TopicSrc<'_> {
102 TopicSrc::Owned(self.to_be_bytes())
103 }
104}
105
106impl<T: IntoTopic + ?Sized> IntoTopic for &T {
107 fn into_topic(&self) -> TopicSrc<'_> {
108 (**self).into_topic()
109 }
110}
111
112pub fn log_event<E: Event>(event: &E) {
121 let n = E::num_topics();
122
123 if n > EVENT_MAX_TOPICS {
124 encoding_event_throw(format_args!(
125 "event declared {n} topics, but the maximum is {EVENT_MAX_TOPICS}"
126 ));
127 }
128
129 let topic = |i| {
130 if i < n {
131 let t = event.topic(i);
132 debug_assert!(
133 t.is_some(),
134 "event declared {n} topics, but topic({i}) returned None"
135 );
136 Some(t?.into())
137 } else {
138 None
139 }
140 };
141
142 let topic0: Option<Topic> = topic(0);
143 let topic1: Option<Topic> = topic(1);
144 let topic2: Option<Topic> = topic(2);
145
146 with_scratch(|buf| {
147 encode_scratch(buf, event).unwrap_or_else(|e| encoding_event_throw(e));
148 sys::uninit_LogEvent(
149 topic0.as_ref(),
150 topic1.as_ref(),
151 topic2.as_ref(),
152 buf.as_slice(),
153 )
154 .or_syscall_throw(SyscallNo::kLogEvent);
155 });
156}