Skip to main content

webx_core/mediator/
default.rs

1//! IMediator implementation.
2//!
3//! `Mediator::send` delegates to the shared [`dispatch`](super::dispatch::dispatch)
4//! function — the same path used by HTTP endpoint adapters after request construction.
5//!
6//! `Mediator::publish` resolves `IEventHandler<T>` from the DI container and
7//! invokes all registered handlers.
8
9use std::any::Any;
10use std::sync::Arc;
11
12use rust_dix::ScopeFactory;
13
14use super::dispatch;
15use super::{IEventRequest, IMediator, IRequest};
16use crate::error::Result;
17use crate::handler::IEventHandler;
18use crate::route::scan::HandlerCache;
19
20/// Default implementation of IMediator.
21///
22/// Holds the host's root `ServiceProvider` and its compiled handler registry.
23/// Each `send` call creates a per-request scope, so Scoped services (e.g.
24/// DbContext) are resolved fresh and released with the scope by rust-dix.
25pub struct Mediator {
26    provider: Arc<rust_dix::ServiceProvider>,
27    handler_cache: Arc<HandlerCache>,
28}
29
30impl Mediator {
31    /// Create a mediator over a host's provider and compiled handler registry.
32    ///
33    /// `Host::build` constructs these handles once;
34    /// [`IServiceCollectionExt::add_mediator`](crate::route::ext::IServiceCollectionExt::add_mediator)
35    /// registers a transient mediator that resolves them from DI, so handler
36    /// code can inject `Arc<dyn IMediator>` / `Mediator` and use the same path
37    /// as in-process calls.
38    pub fn new(provider: Arc<rust_dix::ServiceProvider>, handler_cache: Arc<HandlerCache>) -> Self {
39        Self {
40            provider,
41            handler_cache,
42        }
43    }
44}
45
46#[async_trait::async_trait]
47impl IMediator for Mediator {
48    async fn send<T, R>(&self, req: T) -> Result<R>
49    where
50        T: IRequest<R> + Send + 'static,
51        R: Send + 'static,
52    {
53        dispatch::dispatch(&self.provider, &self.handler_cache, req).await
54    }
55
56    async fn send_boxed(&self, req: Box<dyn Any + Send>) -> Result<Box<dyn Any + Send>> {
57        let type_id = (*req).type_id();
58        let dispatch_fn = self.handler_cache.boxed_dispatch(type_id).ok_or_else(|| {
59            crate::Error::Di(format!(
60                "No #[handler] with a boxed dispatch bridge is registered for request type {:?}",
61                type_id
62            ))
63        })?;
64        dispatch_fn(
65            Arc::clone(&self.provider),
66            Arc::clone(&self.handler_cache),
67            req,
68        )
69        .await
70    }
71
72    async fn publish<T: IEventRequest>(&self, event: T) -> Result<()> {
73        let scope = self.provider.create_scope();
74        let handlers: Vec<Arc<dyn IEventHandler<T>>> = scope.get_all::<dyn IEventHandler<T>>();
75
76        let mut first_err: Option<crate::error::Error> = None;
77        for handler in handlers {
78            if let Err(e) = handler.handle(event.clone()).await {
79                if first_err.is_none() {
80                    first_err = Some(e);
81                }
82            }
83        }
84
85        match first_err {
86            Some(e) => Err(e),
87            None => Ok(()),
88        }
89    }
90}