use crate::ctx::Ctx;
use crate::extract::{FromContext, Reject};
use futures::future::BoxFuture;
use std::future::Future;
use std::sync::Arc;
pub type HandlerResult = nagisa_types::error::Result<()>;
pub trait IntoHandlerResult {
fn into_handler_result(self) -> HandlerResult;
}
impl IntoHandlerResult for () {
fn into_handler_result(self) -> HandlerResult {
Ok(())
}
}
impl IntoHandlerResult for HandlerResult {
fn into_handler_result(self) -> HandlerResult {
self
}
}
pub enum HandlerOutcome {
Handled,
Skipped,
Errored(nagisa_types::error::Error),
}
pub trait ErasedHandler: Send + Sync {
fn call(&self, ctx: Arc<Ctx>) -> BoxFuture<'static, HandlerOutcome>;
}
pub trait Handler<Args>: Clone + Send + Sync + 'static {
fn call(&self, ctx: Arc<Ctx>) -> BoxFuture<'static, HandlerOutcome>;
fn erased(self) -> Arc<dyn ErasedHandler>
where
Self: Sized,
Args: 'static,
{
Arc::new(HandlerFn { f: self, _args: std::marker::PhantomData })
}
}
struct HandlerFn<F, Args> {
f: F,
_args: std::marker::PhantomData<fn() -> Args>,
}
impl<F, Args> ErasedHandler for HandlerFn<F, Args>
where
F: Handler<Args>,
Args: 'static,
{
fn call(&self, ctx: Arc<Ctx>) -> BoxFuture<'static, HandlerOutcome> {
Handler::call(&self.f, ctx)
}
}
macro_rules! extract_or_bail {
($ty:ty, $ctx:expr) => {
match <$ty as FromContext>::from_context(&$ctx).await {
Ok(v) => v,
Err(Reject::Skip) => {
if $ctx.is_dev() {
tracing::warn!(
extractor = std::any::type_name::<$ty>(),
"[dev] skip: extractor rejected (Skip) — handler not applicable"
);
}
return HandlerOutcome::Skipped;
}
Err(Reject::Error(e)) => return HandlerOutcome::Errored(e),
}
};
}
macro_rules! impl_handler {
($($ty:ident),*) => {
#[allow(non_snake_case, unused_variables)]
impl<F, Fut, Out, $($ty,)*> Handler<($($ty,)*)> for F
where
F: Fn($($ty,)*) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Out> + Send + 'static,
Out: IntoHandlerResult,
$($ty: FromContext + Send + 'static,)*
{
fn call(&self, ctx: Arc<Ctx>) -> BoxFuture<'static, HandlerOutcome> {
let f = self.clone();
Box::pin(async move {
$(let $ty = extract_or_bail!($ty, *ctx);)*
match f($($ty,)*).await.into_handler_result() {
Ok(()) => HandlerOutcome::Handled,
Err(e) => HandlerOutcome::Errored(e),
}
})
}
}
};
}
impl_handler!();
impl_handler!(A0);
impl_handler!(A0, A1);
impl_handler!(A0, A1, A2);
impl_handler!(A0, A1, A2, A3);
impl_handler!(A0, A1, A2, A3, A4);
impl_handler!(A0, A1, A2, A3, A4, A5);
impl_handler!(A0, A1, A2, A3, A4, A5, A6);
impl_handler!(A0, A1, A2, A3, A4, A5, A6, A7);
impl_handler!(A0, A1, A2, A3, A4, A5, A6, A7, A8);
impl_handler!(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9);
impl_handler!(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
impl_handler!(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);