use crate::{Address, B256, Bytes};
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct Log {
pub address: Address,
topics: Vec<B256>,
pub data: Bytes,
}
impl Log {
pub fn new_unchecked(address: Address, topics: Vec<B256>, data: impl Into<Bytes>) -> Self {
Self { address, topics, data: data.into() }
}
pub fn new(address: Address, topics: Vec<B256>, data: impl Into<Bytes>) -> Option<Self> {
let log = Self::new_unchecked(address, topics, data);
log.is_valid().then_some(log)
}
pub fn is_valid(&self) -> bool {
self.topics.len() <= 4
}
pub fn topics(&self) -> &[B256] {
&self.topics
}
pub fn topics_mut(&mut self) -> &mut [B256] {
&mut self.topics
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constructs_from_shared_primitive_fields() {
let address = Address::from_evm_bytes([0x11; 20]);
let topic = B256::from([0x22; 32]);
let log = Log::new(address, vec![topic], b"payload".to_vec()).unwrap();
assert_eq!(log.address, address);
assert_eq!(log.topics(), [topic]);
assert_eq!(log.data.as_ref(), b"payload");
assert!(log.is_valid());
}
#[test]
fn rejects_too_many_topics_but_unchecked_preserves_them() {
let topics = vec![B256::ZERO; 5];
assert!(Log::new(Address::ZERO, topics.clone(), Bytes::new()).is_none());
let log = Log::new_unchecked(Address::ZERO, topics.clone(), Bytes::new());
assert!(!log.is_valid());
assert_eq!(log.topics(), topics);
}
}