use tokio::sync::mpsc;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BlockNotification {
pub number: u64,
pub timestamp: u64,
pub base_fee_per_gas: Option<u64>,
pub gas_used: u64,
pub gas_limit: u64,
}
#[derive(Default)]
pub struct BlockClockPipe {
tx: Option<mpsc::UnboundedSender<BlockNotification>>,
}
impl BlockClockPipe {
pub fn set_channel(&mut self, tx: mpsc::UnboundedSender<BlockNotification>) {
self.tx = Some(tx);
}
pub fn notify(&self, notification: BlockNotification) {
if let Some(ref tx) = self.tx {
let _ = tx.send(notification);
}
}
pub fn close(&mut self) {
self.tx = None;
}
}
#[cfg(test)]
#[expect(clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn notify_delivers_one_notification_per_call() {
let (tx, mut rx) = mpsc::unbounded_channel();
let mut pipe = BlockClockPipe::default();
pipe.set_channel(tx);
let notif = BlockNotification {
number: 25_390_117,
timestamp: 1_700_000_000,
base_fee_per_gas: Some(7_000_000_000),
gas_used: 15_000_000,
gas_limit: 30_000_000,
};
pipe.notify(notif);
let got = rx.try_recv().expect("tick delivered");
assert_eq!(got, notif);
assert!(rx.try_recv().is_err(), "exactly one notification per call");
}
#[test]
fn notify_without_channel_and_after_close_are_quiet_no_ops() {
let mut pipe = BlockClockPipe::default();
let notif = BlockNotification {
number: 1,
timestamp: 0,
base_fee_per_gas: None,
gas_used: 0,
gas_limit: 0,
};
pipe.notify(notif); let (tx, mut rx) = mpsc::unbounded_channel();
pipe.set_channel(tx);
pipe.close();
pipe.notify(notif); assert!(rx.try_recv().is_err(), "closed pipe delivers nothing");
}
#[test]
fn close_ends_the_stream_exactly_once() {
let (tx, mut rx) = mpsc::unbounded_channel();
let mut pipe = BlockClockPipe::default();
pipe.set_channel(tx);
pipe.close();
assert!(rx.try_recv().is_err(), "receiver observes end-of-stream");
}
}