use std::sync::Arc;
use bmux_ipc::Event;
pub trait WireEventSink: Send + Sync {
fn publish(&self, event: Event) -> Result<(), WireEventSinkError>;
}
#[derive(Debug, thiserror::Error)]
pub enum WireEventSinkError {
#[error("wire event sink failed to publish: {0}")]
PublishFailed(String),
}
#[derive(Clone)]
pub struct WireEventSinkHandle(pub Arc<dyn WireEventSink>);
impl WireEventSinkHandle {
#[must_use]
pub fn new<S: WireEventSink + 'static>(sink: S) -> Self {
Self(Arc::new(sink))
}
#[must_use]
pub fn from_arc(sink: Arc<dyn WireEventSink>) -> Self {
Self(sink)
}
#[must_use]
pub fn noop() -> Self {
Self::new(NoopWireEventSink)
}
#[must_use]
pub fn as_dyn(&self) -> &(dyn WireEventSink + 'static) {
self.0.as_ref()
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct NoopWireEventSink;
impl WireEventSink for NoopWireEventSink {
fn publish(&self, _event: Event) -> Result<(), WireEventSinkError> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
struct CapturingSink {
events: Mutex<Vec<Event>>,
}
impl WireEventSink for CapturingSink {
fn publish(&self, event: Event) -> Result<(), WireEventSinkError> {
self.events
.lock()
.map_err(|_| WireEventSinkError::PublishFailed("poisoned".into()))?
.push(event);
Ok(())
}
}
#[test]
fn noop_sink_accepts_any_event() {
let sink = NoopWireEventSink;
assert!(sink.publish(Event::ServerStarted).is_ok());
assert!(sink.publish(Event::ServerStopping).is_ok());
}
#[test]
fn handle_clone_shares_inner_sink() {
let capturing = CapturingSink {
events: Mutex::new(Vec::new()),
};
let handle = WireEventSinkHandle::new(capturing);
let clone = handle.clone();
clone
.as_dyn()
.publish(Event::ServerStarted)
.expect("publish");
handle
.as_dyn()
.publish(Event::ServerStopping)
.expect("publish");
assert_eq!(Arc::strong_count(&handle.0), 2);
}
#[test]
fn noop_handle_constructs_and_publishes() {
let handle = WireEventSinkHandle::noop();
handle
.as_dyn()
.publish(Event::ServerStarted)
.expect("noop publish");
}
}