use super::events::Event;
pub struct OperationResult<T, E, Ev: Event> {
pub result: Result<T, E>,
pub event: Option<Ev>,
}
impl<T, E, Ev: Event> OperationResult<T, E, Ev> {
pub fn success(value: T, event: Option<Ev>) -> Self {
Self {
result: Ok(value),
event,
}
}
pub fn failure(error: E) -> Self {
Self {
result: Err(error),
event: None,
}
}
pub fn into_result(self) -> Result<T, E> {
self.result
}
pub fn publish<B: EventBus<Ev>>(self, bus: &mut B) -> Result<T, E> {
if self.result.is_ok()
&& let Some(event) = self.event
{
bus.publish(&event);
}
self.result
}
}
pub trait EventBus<Ev: Event> {
fn publish(&mut self, event: &Ev);
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq)]
enum TestEvent {
ItemAdded { id: usize },
}
impl Event for TestEvent {}
struct TestEventBus {
log: Vec<TestEvent>,
}
impl EventBus<TestEvent> for TestEventBus {
fn publish(&mut self, event: &TestEvent) {
self.log.push(event.clone());
}
}
#[test]
fn test_successful_operation_publishes_event() {
let mut bus = TestEventBus { log: vec![] };
let result: Result<i32, String> =
OperationResult::success(42, Some(TestEvent::ItemAdded { id: 0 })).publish(&mut bus);
assert_eq!(result, Ok(42));
assert_eq!(bus.log.len(), 1);
assert_eq!(bus.log[0], TestEvent::ItemAdded { id: 0 });
}
#[test]
fn test_failed_operation_does_not_publish() {
let mut bus = TestEventBus { log: vec![] };
let result: Result<(), String> =
OperationResult::failure("error".to_string()).publish(&mut bus);
assert!(result.is_err());
assert_eq!(bus.log.len(), 0);
}
#[test]
fn test_success_without_event() {
let mut bus = TestEventBus { log: vec![] };
let result: Result<(), String> = OperationResult::success((), None).publish(&mut bus);
assert!(result.is_ok());
assert_eq!(bus.log.len(), 0);
}
}