webx_core/mediator/
dispatch.rs1#![allow(deprecated)]
9
10use std::any::Any;
11use std::sync::Arc;
12
13use rust_dix::ScopeFactory;
14
15use crate::dispatch_runtime::dispatch_handler_cache;
16use crate::error::{Error, Result};
17use crate::mediator::pipeline::{build_chain as build_erased_chain, build_typed_chain};
18use crate::mediator::IRequest;
19use crate::pipeline::{
20 BoxedNextFn, BoxedPipelineFuture, IErasedPipelineBehavior, IPipelineBehavior,
21};
22
23pub async fn dispatch<T, R>(provider: &Arc<rust_dix::ServiceProvider>, req: T) -> Result<R>
25where
26 T: IRequest<R> + Send + 'static,
27 R: Send + 'static,
28{
29 let req_type_id = std::any::TypeId::of::<T>();
30
31 let cache = dispatch_handler_cache();
32 let entry = cache.get_by_type_id(req_type_id).ok_or_else(|| {
33 Error::Di(format!(
34 "No #[handler] registered for request type '{}'",
35 std::any::type_name::<T>()
36 ))
37 })?;
38
39 let scope = provider.create_scope();
40 let resolver: &dyn rust_dix::IServiceResolver = &scope;
41 let handler = (entry.factory)(resolver)?;
42
43 let typed_behaviors: Vec<Arc<dyn IPipelineBehavior<T, R>>> =
46 scope.get_all::<dyn IPipelineBehavior<T, R>>();
47 #[allow(deprecated)]
48 let erased_behaviors: Vec<Arc<dyn IErasedPipelineBehavior>> =
49 scope.get_all::<dyn IErasedPipelineBehavior>();
50
51 let entry_call = entry.call;
52
53 let typed_chain = build_typed_chain::<T, R>(typed_behaviors, move |typed_req: T| {
58 Box::pin(async move {
59 let out = (entry_call)(handler, Box::new(typed_req) as Box<dyn Any + Send>).await?;
60 out.downcast::<R>().map(|value| *value).map_err(|_| {
61 Error::Internal(format!(
62 "Handler for `{}` returned a different response type than `{}`",
63 std::any::type_name::<T>(),
64 std::any::type_name::<R>()
65 ))
66 })
67 })
68 });
69
70 #[allow(deprecated)]
73 let erased_terminal: BoxedNextFn =
74 Box::new(move |req: Box<dyn Any + Send>| -> BoxedPipelineFuture {
75 Box::pin(async move {
76 let typed_req = *req.downcast::<T>().map_err(|_| {
77 Error::Internal(format!(
78 "Pipeline request type mismatch: expected `{}`",
79 std::any::type_name::<T>()
80 ))
81 })?;
82 let out = typed_chain.run(typed_req).await?;
83 Ok(Box::new(out) as Box<dyn Any + Send>)
84 })
85 });
86
87 #[allow(deprecated)]
88 let erased_chain = build_erased_chain(erased_behaviors, erased_terminal);
89 let result_box = erased_chain(Box::new(req)).await?;
90 Ok(*result_box.downcast::<R>().map_err(|_| {
91 Error::Internal(format!(
95 "Response type mismatch: the pipeline returned a different type than the handler \
96 expects (expected `{}`). A legacy `IErasedPipelineBehavior` must not short-circuit \
97 with a different response type; return the handler's response type or call `next`. \
98 Typed `IPipelineBehavior<Req, Rsp>` values are checked at compile time.",
99 std::any::type_name::<R>()
100 ))
101 })?)
102}