1use crate::ctx::Ctx;
4use crate::extract::{FromContext, Reject};
5use futures::future::BoxFuture;
6use std::future::Future;
7use std::sync::Arc;
8
9pub type HandlerResult = nagisa_types::error::Result<()>;
11
12pub trait IntoHandlerResult {
22 fn into_handler_result(self) -> HandlerResult;
23}
24
25impl IntoHandlerResult for () {
26 fn into_handler_result(self) -> HandlerResult {
27 Ok(())
28 }
29}
30
31impl IntoHandlerResult for HandlerResult {
32 fn into_handler_result(self) -> HandlerResult {
33 self
34 }
35}
36
37pub enum HandlerOutcome {
39 Handled,
41 Skipped,
43 Errored(nagisa_types::error::Error),
45}
46
47pub trait ErasedHandler: Send + Sync {
49 fn call(&self, ctx: Arc<Ctx>) -> BoxFuture<'static, HandlerOutcome>;
50}
51
52pub trait Handler<Args>: Clone + Send + Sync + 'static {
54 fn call(&self, ctx: Arc<Ctx>) -> BoxFuture<'static, HandlerOutcome>;
55
56 fn erased(self) -> Arc<dyn ErasedHandler>
58 where
59 Self: Sized,
60 Args: 'static,
61 {
62 Arc::new(HandlerFn { f: self, _args: std::marker::PhantomData })
63 }
64}
65
66struct HandlerFn<F, Args> {
71 f: F,
72 _args: std::marker::PhantomData<fn() -> Args>,
73}
74
75impl<F, Args> ErasedHandler for HandlerFn<F, Args>
76where
77 F: Handler<Args>,
78 Args: 'static,
79{
80 fn call(&self, ctx: Arc<Ctx>) -> BoxFuture<'static, HandlerOutcome> {
81 Handler::call(&self.f, ctx)
82 }
83}
84
85macro_rules! extract_or_bail {
90 ($ty:ty, $ctx:expr) => {
91 match <$ty as FromContext>::from_context(&$ctx).await {
92 Ok(v) => v,
93 Err(Reject::Skip) => {
94 if $ctx.is_dev() {
95 tracing::warn!(
96 extractor = std::any::type_name::<$ty>(),
97 "[dev] skip: extractor rejected (Skip) — handler not applicable"
98 );
99 }
100 return HandlerOutcome::Skipped;
101 }
102 Err(Reject::Error(e)) => return HandlerOutcome::Errored(e),
103 }
104 };
105}
106
107macro_rules! impl_handler {
109 ($($ty:ident),*) => {
110 #[allow(non_snake_case, unused_variables)]
111 impl<F, Fut, Out, $($ty,)*> Handler<($($ty,)*)> for F
112 where
113 F: Fn($($ty,)*) -> Fut + Clone + Send + Sync + 'static,
114 Fut: Future<Output = Out> + Send + 'static,
115 Out: IntoHandlerResult,
116 $($ty: FromContext + Send + 'static,)*
117 {
118 fn call(&self, ctx: Arc<Ctx>) -> BoxFuture<'static, HandlerOutcome> {
119 let f = self.clone();
120 Box::pin(async move {
121 $(let $ty = extract_or_bail!($ty, *ctx);)*
122 match f($($ty,)*).await.into_handler_result() {
124 Ok(()) => HandlerOutcome::Handled,
125 Err(e) => HandlerOutcome::Errored(e),
126 }
127 })
128 }
129 }
130 };
131}
132
133impl_handler!();
134impl_handler!(A0);
135impl_handler!(A0, A1);
136impl_handler!(A0, A1, A2);
137impl_handler!(A0, A1, A2, A3);
138impl_handler!(A0, A1, A2, A3, A4);
139impl_handler!(A0, A1, A2, A3, A4, A5);
140impl_handler!(A0, A1, A2, A3, A4, A5, A6);
141impl_handler!(A0, A1, A2, A3, A4, A5, A6, A7);
142impl_handler!(A0, A1, A2, A3, A4, A5, A6, A7, A8);
143impl_handler!(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9);
144impl_handler!(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
145impl_handler!(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);