use super::event::EngineEvent;
pub trait EngineSink: Send + Sync {
fn emit(&self, event: EngineEvent);
}
pub struct NullSink;
impl EngineSink for NullSink {
fn emit(&self, _event: EngineEvent) {}
}
#[cfg(any(test, feature = "test-support"))]
#[derive(Debug, Default)]
pub struct TestSink {
events: std::sync::Mutex<Vec<EngineEvent>>,
}
#[cfg(any(test, feature = "test-support"))]
impl TestSink {
pub fn new() -> Self {
Self::default()
}
pub fn events(&self) -> Vec<EngineEvent> {
self.events.lock().unwrap().clone()
}
pub fn len(&self) -> usize {
self.events.lock().unwrap().len()
}
pub fn is_empty(&self) -> bool {
self.events.lock().unwrap().is_empty()
}
}
#[cfg(any(test, feature = "test-support"))]
impl EngineSink for TestSink {
fn emit(&self, event: EngineEvent) {
self.events.lock().unwrap().push(event);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sink_collects_events() {
let sink = TestSink::new();
assert!(sink.is_empty());
sink.emit(EngineEvent::ResponseStart);
sink.emit(EngineEvent::TextDelta {
text: "hello".into(),
});
sink.emit(EngineEvent::TextDone);
assert_eq!(sink.len(), 3);
let events = sink.events();
assert!(matches!(events[0], EngineEvent::ResponseStart));
assert!(matches!(&events[1], EngineEvent::TextDelta { text } if text == "hello"));
assert!(matches!(events[2], EngineEvent::TextDone));
}
#[test]
fn test_sink_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<TestSink>();
}
#[test]
fn test_trait_object_works() {
let sink: Box<dyn EngineSink> = Box::new(TestSink::new());
sink.emit(EngineEvent::Info {
message: "test".into(),
});
}
}