1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//! Pipeline behavior trait: IPipelineBehavior.
use crateResult;
use Any;
use Future;
use Pin;
/// Type-erased continuation function for pipeline behaviors.
///
/// Carries a boxed request to the next behavior (or terminal handler).
pub type BoxedNextFn = ;
/// Boxed future returned by a pipeline behavior step.
pub type BoxedPipelineFuture = ;
/// Pipeline behavior that wraps around request handling.
///
/// Multiple behaviors form a chain. Each behavior can:
/// - Inspect or modify the request before passing it on
/// - Inspect or modify the response after it returns
/// - Short-circuit and skip the rest of the chain (by not calling `next`)
///
/// Analogous to MediatR's `IPipelineBehavior<TRequest, TResponse>`.
///
/// # Lifetime and dependencies
///
/// Behaviors are registered as **singletons**: one instance per host, shared by
/// every request. The DI resolver is available only while the behavior is being
/// **constructed** — use [`IServiceCollectionExt::add_pipeline_with`] and take
/// your dependencies in the factory closure:
///
/// ```ignore
/// svc.add_pipeline_with(|resolver| {
/// // `&dyn IServiceResolver` is object-safe: resolve type-erased.
/// let clock: Arc<Clock> = resolver
/// .get_any(std::any::type_name::<Clock>())
/// .and_then(|service| service.downcast::<Arc<Clock>>().ok())
/// .map(|service| Arc::clone(&*service))
/// .expect("register Clock before the pipeline");
/// Arc::new(TimingBehavior { clock }) as Arc<dyn IPipelineBehavior>
/// })
/// ```
///
/// Inside [`IPipelineBehavior::handle`] there is no resolver and no typed
/// request/response: the request and the eventual response are
/// `Box<dyn Any + Send>`. Cross-cutting mechanics (timing, logging, cancellation)
/// belong here; business rules that need the typed request belong in the handler
/// or a use case it calls. [`IServiceCollectionExt::add_pipeline`] remains the
/// zero-dependency convenience registration for `Default` behaviors.
///
/// # Response type contract
///
/// A behavior that short-circuits **must** return the same response type the
/// handler would return (`downcast` to it happens after the chain, before the
/// caller sees the value). Short-circuiting with a different type — for example
/// `Box::new(())` — fails with an explicit type-mismatch error instead of
/// silently succeeding.
///
/// [`IServiceCollectionExt::add_pipeline_with`]: crate::route::ext::IServiceCollectionExt::add_pipeline_with
/// [`IServiceCollectionExt::add_pipeline`]: crate::route::ext::IServiceCollectionExt::add_pipeline