use crate::call::with_scratch;
use crate::exn::*;
use crate::internal::encode_scratch;
use crate::re_throw::OrSyscallThrow;
use crate::sys::{self, SyscallNo};
pub use thruput_event::{EmittedEvent, Event, Topic, TopicSrc, TypedEvent};
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be an event topic",
// No list of accepted types: the impls are explicit, so rustc prints them
// itself. A hand-kept copy only drifts.
note = "either widen the field to a listed type, or drop `#[topic]` and let it ride in the event payload, where it stays readable"
)]
pub trait IntoTopic {
#[allow(clippy::wrong_self_convention)]
fn into_topic(&self) -> TopicSrc<'_>;
}
impl IntoTopic for [u8; 32] {
fn into_topic(&self) -> TopicSrc<'_> {
TopicSrc::Shared(self)
}
}
pub const EVENT_MAX_TOPICS: u32 = thruput_xdr::event::kMaxTopics;
pub const EVENT_MAX_DATA: u32 = thruput_xdr::event::kMaxEventLength;
pub fn hash_to_topic<T: crate::WriteXDR>(val: &T) -> Topic {
with_scratch(|buf| {
encode_scratch(buf, val).unwrap_or_else(|e| encoding_event_throw(e));
crate::crypto::sha256(buf.as_slice()).into_inner()
})
}
macro_rules! impl_into_topic_direct {
($wide:ty; $($ty:ty),*) => {
$(
impl IntoTopic for $ty {
fn into_topic(&self) -> TopicSrc<'_> {
let value: $wide = (*self).into();
TopicSrc::Owned(value.to_be_bytes())
}
}
)*
};
}
impl_into_topic_direct!(crate::U256; u128, u64, u32, bool);
impl_into_topic_direct!(crate::I256; i128, i64, i32);
impl IntoTopic for str {
fn into_topic(&self) -> TopicSrc<'_> {
TopicSrc::Owned(hash_to_topic(&self))
}
}
impl IntoTopic for [u8] {
fn into_topic(&self) -> TopicSrc<'_> {
TopicSrc::Owned(hash_to_topic(&self))
}
}
impl IntoTopic for crate::types::ContractAddress {
fn into_topic(&self) -> TopicSrc<'_> {
(**self).into_topic()
}
}
impl IntoTopic for crate::types::SHA256Hash {
fn into_topic(&self) -> TopicSrc<'_> {
(**self).into_topic()
}
}
impl IntoTopic for crate::U256 {
fn into_topic(&self) -> TopicSrc<'_> {
TopicSrc::Owned(self.to_be_bytes())
}
}
impl IntoTopic for crate::I256 {
fn into_topic(&self) -> TopicSrc<'_> {
TopicSrc::Owned(self.to_be_bytes())
}
}
impl<T: IntoTopic + ?Sized> IntoTopic for &T {
fn into_topic(&self) -> TopicSrc<'_> {
(**self).into_topic()
}
}
pub fn log_event<E: Event>(event: &E) {
let n = E::num_topics();
if n > EVENT_MAX_TOPICS {
encoding_event_throw(format_args!(
"event declared {n} topics, but the maximum is {EVENT_MAX_TOPICS}"
));
}
let topic = |i| {
if i < n {
let t = event.topic(i);
debug_assert!(
t.is_some(),
"event declared {n} topics, but topic({i}) returned None"
);
Some(t?.into())
} else {
None
}
};
let topic0: Option<Topic> = topic(0);
let topic1: Option<Topic> = topic(1);
let topic2: Option<Topic> = topic(2);
with_scratch(|buf| {
encode_scratch(buf, event).unwrap_or_else(|e| encoding_event_throw(e));
sys::uninit_LogEvent(
topic0.as_ref(),
topic1.as_ref(),
topic2.as_ref(),
buf.as_slice(),
)
.or_syscall_throw(SyscallNo::kLogEvent);
});
}