use std::{fmt, marker};
use crate::{BindCtx, Ctx, IntoService, IntoServiceFactory, Service, ServiceFactory};
use crate::{ServiceCaller, ServiceChain, ServiceChainFactory};
pub fn apply_fn<S, St, Req, F, In, Out, Err>(
service: impl IntoService<S, St, Req>,
f: F,
) -> ServiceChain<Apply<S, St, Req, F, In, Out, Err>, St, In>
where
S: Service<St, Req>,
F: AsyncFn(In, &ApplyCtx<'_, S, St, Req>) -> Result<Out, Err>,
Err: From<S::Error>,
{
crate::service(Apply::new(service.into_service(), f))
}
pub fn apply_fn_factory<Sf, St, Req, F, In, Out, Err>(
service: impl IntoServiceFactory<Sf, St, Req>,
f: F,
) -> ServiceChainFactory<ApplyFactory<F, Sf, St, Req, In, Out, Err>, St, In>
where
Sf: ServiceFactory<St, Req>,
F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
Err: From<Sf::Error>,
{
crate::factory(ApplyFactory::new(service.into_factory(), f))
}
#[derive(Debug)]
pub struct ApplyCtx<'a, S, St, Req> {
ctx: Ctx<'a, S, St>,
service: &'a S,
r: marker::PhantomData<Req>,
}
impl<S: Service<St, Req>, St, Req> ApplyCtx<'_, S, St, Req> {
#[inline]
pub fn st(&self) -> &St {
self.ctx.st()
}
#[inline]
pub async fn call(&self, req: Req) -> Result<S::Res, S::Error> {
self.service.call(req, self.ctx.cast()).await
}
}
impl<S: Service<St, Req>, St, Req> ServiceCaller<Req, S::Res, S::Error>
for ApplyCtx<'_, S, St, Req>
{
#[inline]
async fn call_service(&self, req: Req) -> Result<S::Res, S::Error> {
self.call(req).await
}
}
pub struct Apply<S, St, Req, F, In, Out, Err> {
svc: S,
f: F,
r: marker::PhantomData<fn(St, Req) -> (In, Out, Err)>,
}
impl<S, St, Req, F, In, Out, Err> Apply<S, St, Req, F, In, Out, Err>
where
F: AsyncFn(In, &ApplyCtx<'_, S, St, Req>) -> Result<Out, Err>,
{
pub(crate) fn new(svc: S, f: F) -> Self {
Apply {
f,
svc,
r: marker::PhantomData,
}
}
}
impl<S, St, Req, F, In, Out, Err> Clone for Apply<S, St, Req, F, In, Out, Err>
where
S: Clone,
F: Clone,
{
fn clone(&self) -> Self {
Apply {
svc: self.svc.clone(),
f: self.f.clone(),
r: marker::PhantomData,
}
}
}
impl<S, St, Req, F, In, Out, Err> fmt::Debug for Apply<S, St, Req, F, In, Out, Err>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Apply")
.field("svc", &self.svc)
.field("map", &std::any::type_name::<F>())
.finish()
}
}
impl<S, St, Req, F, In, Out, Err> Service<St, In> for Apply<S, St, Req, F, In, Out, Err>
where
S: Service<St, Req>,
F: AsyncFn(In, &ApplyCtx<'_, S, St, Req>) -> Result<Out, Err>,
Err: From<S::Error>,
{
type Res = Out;
type Error = Err;
#[inline]
async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), Err> {
ctx.ready(&self.svc).await.map_err(From::from)
}
#[inline]
async fn call(&self, req: In, ctx: Ctx<'_, Self, St>) -> Result<Out, Err> {
let ctx = ApplyCtx {
ctx: ctx.cast(),
service: &self.svc,
r: marker::PhantomData,
};
(self.f)(req, &ctx).await
}
#[inline]
fn bind(&self, ctx: BindCtx<'_>) {
ctx.bind(&self.svc);
}
#[inline]
async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
ctx.shutdown(&self.svc).await;
}
}
pub struct ApplyFactory<F, Sf, St, Req, In, Out, Err>
where
F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
Sf: ServiceFactory<St, Req>,
{
f: F,
sf: Sf,
r: marker::PhantomData<fn(St, Req) -> (In, Out)>,
}
impl<F, Sf, St, Req, In, Out, Err> ApplyFactory<F, Sf, St, Req, In, Out, Err>
where
F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
Sf: ServiceFactory<St, Req>,
{
pub(crate) fn new(sf: Sf, f: F) -> Self
where
Sf: ServiceFactory<St, Req>,
Err: From<Sf::Error>,
{
Self {
f,
sf,
r: marker::PhantomData,
}
}
}
impl<F, Sf, St, Req, In, Out, Err> Clone for ApplyFactory<F, Sf, St, Req, In, Out, Err>
where
F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
Sf: ServiceFactory<St, Req> + Clone,
{
fn clone(&self) -> Self {
Self {
f: self.f.clone(),
sf: self.sf.clone(),
r: marker::PhantomData,
}
}
}
impl<F, Sf, St, Req, In, Out, Err> fmt::Debug for ApplyFactory<F, Sf, St, Req, In, Out, Err>
where
F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
Sf: ServiceFactory<St, Req> + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ApplyFactory")
.field("factory", &self.sf)
.field("map", &std::any::type_name::<F>())
.finish()
}
}
impl<F, Sf, St, Req, In, Out, Err> ServiceFactory<St, In>
for ApplyFactory<F, Sf, St, Req, In, Out, Err>
where
F: AsyncFn(In, &ApplyCtx<'_, Sf::Service, St, Req>) -> Result<Out, Err> + Clone,
Sf: ServiceFactory<St, Req>,
Err: From<Sf::Error>,
{
type Res = Out;
type Error = Err;
type Service = Apply<Sf::Service, St, Req, F, In, Out, Err>;
type InitError = Sf::InitError;
#[inline]
async fn create(&self, st: &St) -> Result<Self::Service, Self::InitError> {
self.sf
.create(st)
.await
.map(|svc| Apply::new(svc, self.f.clone()))
}
}
#[cfg(test)]
#[path = "../tests/unit/apply.rs"]
mod tests;