Skip to main content

rust_webx_core/mediator/
dispatch.rs

1//! Shared request dispatch used by `Mediator::send` and HTTP endpoint adapters.
2//!
3//! Looks up the `HandlerRegistration` (collected via `#[handler]`) by request type,
4//! creates a per-request DI scope, runs the `IPipelineBehavior` chain, and invokes
5//! the handler through the type-erased call bridge.
6
7use std::any::Any;
8use std::sync::Arc;
9
10use rust_dix::ScopeFactory;
11
12use crate::error::Result;
13use crate::mediator::IRequest;
14use crate::mediator::pipeline::build_chain as build_pipeline_chain;
15use crate::pipeline::BoxedNextFn;
16use crate::route::scan::HandlerCache;
17
18/// Dispatch a request through the handler registry, pipeline, and call bridge.
19pub async fn dispatch<T, R>(provider: &Arc<rust_dix::ServiceProvider>, req: T) -> Result<R>
20where
21    T: IRequest<R> + Send + 'static,
22    R: serde::Serialize + Send + 'static,
23{
24    let req_type_id = std::any::TypeId::of::<T>();
25
26    let cache = HandlerCache::get_or_init();
27    let entry = cache.get_by_type_id(req_type_id).ok_or_else(|| {
28        crate::Error::Di(format!(
29            "No #[handler] registered for request type '{}'",
30            std::any::type_name::<T>()
31        ))
32    })?;
33
34    let scope = provider.create_scope();
35    let resolver: &dyn rust_dix::IServiceResolver = &scope;
36    let handler = (entry.factory)(resolver)?;
37
38    let behaviors: Vec<Arc<dyn crate::pipeline::IPipelineBehavior>> =
39        scope.get_all::<dyn crate::pipeline::IPipelineBehavior>();
40
41    let entry_call = entry.call;
42    let terminal: BoxedNextFn = Box::new(
43        move |req: Box<dyn Any + Send>| -> crate::pipeline::BoxedPipelineFuture {
44            Box::pin(async move { (entry_call)(handler, req).await })
45        },
46    );
47
48    let chain = build_pipeline_chain(behaviors, terminal);
49    let result_box = chain(Box::new(req)).await?;
50    Ok(*result_box.downcast::<R>().map_err(|_| {
51        crate::Error::Internal("Response type mismatch in dispatch call bridge".to_string())
52    })?)
53}