#![allow(non_snake_case)]
use std::{fmt, marker::PhantomData, task::Poll};
use ntex_service::{Ctx, IntoService, IntoServiceFactory, Service, ServiceFactory};
pub fn variant<V1: Service<St, V1R>, St, V1R>(
f: impl IntoService<V1, St, V1R>,
) -> Variant<St, V1, V1R> {
Variant {
service: f.into_service(),
_t: PhantomData,
}
}
pub struct Variant<St, A, AR> {
service: A,
_t: PhantomData<(St, AR)>,
}
impl<St, A, AR> Variant<St, A, AR>
where
A: Service<St, AR>,
{
pub fn v2<B, BR>(self, f: impl IntoService<B, St, BR>) -> VariantService2<St, A, B, AR, BR>
where
B: Service<St, BR, Res = A::Res, Error = A::Error>,
{
VariantService2 {
V1: self.service,
V2: f.into_service(),
_t: PhantomData,
}
}
}
impl<St, A, AR> fmt::Debug for Variant<St, A, AR>
where
A: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Variant")
.field("V1", &self.service)
.finish()
}
}
pub fn variant_factory<V1: ServiceFactory<St, V1R, Cfg>, St, V1R, Cfg>(
f: impl IntoServiceFactory<V1, St, V1R, Cfg>,
) -> VariantFactory<St, Cfg, V1, V1R> {
VariantFactory {
factory: f.into_factory(),
_t: PhantomData,
}
}
pub struct VariantFactory<St, Cfg, A, AR> {
factory: A,
_t: PhantomData<(St, Cfg, AR)>,
}
impl<St, Cfg, A, AR> VariantFactory<St, Cfg, A, AR>
where
A: ServiceFactory<St, AR, Cfg>,
{
pub fn v2<B, BR>(
self,
f: impl IntoServiceFactory<B, St, BR, Cfg>,
) -> VariantFactory2<St, Cfg, A, B, AR, BR>
where
B: ServiceFactory<St, BR, Cfg, Res = A::Res, Error = A::Error, InitError = A::InitError>,
{
VariantFactory2 {
V1: self.factory,
V2: f.into_factory(),
_t: PhantomData,
}
}
}
impl<St, Cfg, A, AR> fmt::Debug for VariantFactory<St, Cfg, A, AR>
where
A: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Variant")
.field("V1", &self.factory)
.finish()
}
}
macro_rules! variant_impl_and ({$fac1_type:ident, $fac2_type:ident, $name:ident, $r_name:ident, $m_name:ident, ($($T:ident),+), ($($R:ident),+)} => {
#[allow(non_snake_case)]
impl<St, Cfg, V1, $($T,)+ V1R, $($R,)+> $fac1_type<St, Cfg, V1, $($T,)+ V1R, $($R,)+>
where
V1: ServiceFactory<St, V1R>,
{
pub fn $m_name<$name, $r_name, F>(self, factory: F) -> $fac2_type<St, Cfg, V1, $($T,)+ $name, V1R, $($R,)+ $r_name>
where $name: ServiceFactory<St, $r_name, Cfg,
Res = V1::Res,
Error = V1::Error,
InitError = V1::InitError>,
F: IntoServiceFactory<$name, St, $r_name, Cfg>,
{
$fac2_type {
V1: self.V1,
$($T: self.$T,)+
$name: factory.into_factory(),
_t: PhantomData
}
}
}
});
macro_rules! variant_impl_and_svc ({$svc1_type:ident, $svc2_type:ident, $name:ident, $r_name:ident, $m_name:ident, ($($T:ident),+), ($($R:ident),+)} => {
#[allow(non_snake_case)]
impl<St, V1, $($T,)+ V1R, $($R,)+> $svc1_type<St, V1, $($T,)+ V1R, $($R,)+>
where
V1: Service<St, V1R>,
{
pub fn $m_name<$name, $r_name, F>(self, service: F) -> $svc2_type<St, V1, $($T,)+ $name, V1R, $($R,)+ $r_name>
where
$name: Service<St, $r_name, Res = V1::Res, Error = V1::Error>,
F: IntoService<$name, St, $r_name>,
{
$svc2_type {
V1: self.V1,
$($T: self.$T,)+
$name: service.into_service(),
_t: PhantomData
}
}
}
});
macro_rules! variant_impl ({$mod_name:ident, $enum_type:ident, $srv_type:ident, $fac_type:ident, $num:literal, $(($n:tt, $T:ident, $R:ident)),+} => {
#[allow(non_snake_case, missing_debug_implementations)]
pub enum $enum_type<V1R, $($R),+> {
V1(V1R),
$($T($R),)+
}
#[allow(non_snake_case)]
pub struct $srv_type<St, V1, $($T,)+ V1R, $($R,)+> {
V1: V1,
$($T: $T,)+
_t: PhantomData<(St, V1R, $($R),+)>,
}
impl<St, V1: Clone, $($T: Clone,)+ V1R, $($R,)+> Clone for $srv_type<St, V1, $($T,)+ V1R, $($R,)+> {
fn clone(&self) -> Self {
Self {
_t: PhantomData,
V1: self.V1.clone(),
$($T: self.$T.clone(),)+
}
}
}
impl<St, V1: fmt::Debug, $($T: fmt::Debug,)+ V1R, $($R,)+> fmt::Debug for $srv_type<St, V1, $($T,)+ V1R, $($R,)+> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(stringify!($srv_type))
.field("V1", &self.V1)
$(.field(stringify!($T), &self.$T))+
.finish()
}
}
impl<St, V1, $($T,)+ V1R, $($R,)+> Service<St, $enum_type<V1R, $($R,)+>> for $srv_type<St, V1, $($T,)+ V1R, $($R,)+>
where
V1: Service<St, V1R>,
$($T: Service<St, $R, Res = V1::Res, Error = V1::Error>),+
{
type Res = V1::Res;
type Error = V1::Error;
async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), Self::Error> {
use std::{future::Future, pin::Pin};
let mut fut1 = ::std::pin::pin!(ctx.ready(&self.V1));
$(let mut $T = ::std::pin::pin!(ctx.ready(&self.$T));)+
let mut ready: [bool; $num] = [false; $num];
::std::future::poll_fn(|cx| {
if !ready[$num-1] {
ready[$num-1] = Pin::new(&mut fut1).poll(cx)?.is_ready();
}
$(if !ready[$n] {
ready[$n] = Pin::new(&mut $T).poll(cx)?.is_ready();
})+;
for v in &ready[..] {
if !v {
return Poll::Pending
}
}
Poll::Ready(Ok(()))
}).await
}
async fn call(&self, req: $enum_type<V1R, $($R,)+>, ctx: Ctx<'_, Self, St>) -> Result<Self::Res, Self::Error> {
match req {
$enum_type::V1(req) => ctx.call(&self.V1, req).await,
$($enum_type::$T(req) => ctx.call(&self.$T, req).await,)+
}
}
async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
ctx.shutdown(&self.V1).await;
$(ctx.shutdown(&&self.$T).await;)+
}
}
#[allow(non_snake_case)]
pub struct $fac_type<St, Cfg, V1, $($T,)+ V1R, $($R,)+> {
V1: V1,
$($T: $T,)+
_t: PhantomData<(St, Cfg, V1R, $($R,)+)>,
}
impl<St, Cfg, V1: Clone, $($T: Clone,)+ V1R, $($R,)+> Clone for $fac_type<St, Cfg, V1, $($T,)+ V1R, $($R,)+> {
fn clone(&self) -> Self {
Self {
_t: PhantomData,
V1: self.V1.clone(),
$($T: self.$T.clone(),)+
}
}
}
impl<St, Cfg, V1: fmt::Debug, $($T: fmt::Debug,)+ V1R, $($R,)+> fmt::Debug for $fac_type<St, Cfg, V1, $($T,)+ V1R, $($R,)+> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Variant")
.field("V1", &self.V1)
$(.field(stringify!($T), &self.$T))+
.finish()
}
}
impl<St, Cfg, V1, $($T,)+ V1R, $($R,)+> ServiceFactory<St, $enum_type<V1R, $($R),+>, Cfg> for $fac_type<St, Cfg, V1, $($T,)+ V1R, $($R,)+>
where
V1: ServiceFactory<St, V1R, Cfg>,
$($T: ServiceFactory<St, $R, Cfg, Res = V1::Res, Error = V1::Error, InitError = V1::InitError>),+
{
type Res = V1::Res;
type Error = V1::Error;
type Service = $srv_type<St, V1::Service, $($T::Service,)+ V1R, $($R,)+>;
type InitError = V1::InitError;
async fn create(&self, cfg: &Cfg) -> Result<Self::Service, Self::InitError> {
Ok($srv_type {
V1: self.V1.create(cfg).await?,
$($T: self.$T.create(cfg).await?,)+
_t: PhantomData
})
}
}
});
#[rustfmt::skip]
variant_impl!(v2, Variant2, VariantService2, VariantFactory2, 2, (0, V2, V2R));
#[rustfmt::skip]
variant_impl!(v3, Variant3, VariantService3, VariantFactory3, 3, (0, V2, V2R), (1, V3, V3R));
#[rustfmt::skip]
variant_impl!(v4, Variant4, VariantService4, VariantFactory4, 4, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R));
#[rustfmt::skip]
variant_impl!(v5, Variant5, VariantService5, VariantFactory5, 5, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R), (3, V5, V5R));
#[rustfmt::skip]
variant_impl!(v6, Variant6, VariantService6, VariantFactory6, 6, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R), (3, V5, V5R), (4, V6, V6R));
#[rustfmt::skip]
variant_impl!(v7, Variant7, VariantService7, VariantFactory7, 7, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R), (3, V5, V5R), (4, V6, V6R), (5, V7, V7R));
#[rustfmt::skip]
variant_impl!(v8, Variant8, VariantService8, VariantFactory8, 8, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R), (3, V5, V5R), (4, V6, V6R), (5, V7, V7R), (6, V8, V8R));
#[rustfmt::skip]
variant_impl_and_svc!(VariantService2, VariantService3, V3, V3R, v3, (V2), (V2R));
#[rustfmt::skip]
variant_impl_and_svc!(VariantService3, VariantService4, V4, V4R, v4, (V2, V3), (V2R, V3R));
#[rustfmt::skip]
variant_impl_and_svc!(VariantService4, VariantService5, V5, V5R, v5, (V2, V3, V4), (V2R, V3R, V4R));
#[rustfmt::skip]
variant_impl_and_svc!(VariantService5, VariantService6, V6, V6R, v6, (V2, V3, V4, V5), (V2R, V3R, V4R, V5R));
#[rustfmt::skip]
variant_impl_and_svc!(VariantService6, VariantService7, V7, V7R, v7, (V2, V3, V4, V5, V6), (V2R, V3R, V4R, V5R, V6R));
#[rustfmt::skip]
variant_impl_and_svc!(VariantService7, VariantService8, V8, V8R, v8, (V2, V3, V4, V5, V6, V7), (V2R, V3R, V4R, V5R, V6R, V7R));
#[rustfmt::skip]
variant_impl_and!(VariantFactory2, VariantFactory3, V3, V3R, v3, (V2), (V2R));
#[rustfmt::skip]
variant_impl_and!(VariantFactory3, VariantFactory4, V4, V4R, v4, (V2, V3), (V2R, V3R));
#[rustfmt::skip]
variant_impl_and!(VariantFactory4, VariantFactory5, V5, V5R, v5, (V2, V3, V4), (V2R, V3R, V4R));
#[rustfmt::skip]
variant_impl_and!(VariantFactory5, VariantFactory6, V6, V6R, v6, (V2, V3, V4, V5), (V2R, V3R, V4R, V5R));
#[rustfmt::skip]
variant_impl_and!(VariantFactory6, VariantFactory7, V7, V7R, v7, (V2, V3, V4, V5, V6), (V2R, V3R, V4R, V5R, V6R));
#[rustfmt::skip]
variant_impl_and!(VariantFactory7, VariantFactory8, V8, V8R, v8, (V2, V3, V4, V5, V6, V7), (V2R, V3R, V4R, V5R, V6R, V7R));
#[cfg(test)]
mod tests {
#![allow(clippy::unused_async_trait_impl)]
use ntex_service::{Pipeline, fn_factory, fn_service};
use super::*;
use crate::time;
#[derive(Debug, Clone)]
struct Srv1;
impl Service<(), ()> for Srv1 {
type Res = usize;
type Error = ();
async fn ready(&self, _: Ctx<'_, Self>) -> Result<(), Self::Error> {
Ok(())
}
async fn call(&self, (): (), _: Ctx<'_, Self>) -> Result<usize, ()> {
Ok(1)
}
}
#[derive(Debug, Clone)]
struct Srv2;
impl Service<(), ()> for Srv2 {
type Res = usize;
type Error = ();
async fn ready(&self, _: Ctx<'_, Self>) -> Result<(), Self::Error> {
Ok(())
}
async fn call(&self, (): (), _: Ctx<'_, Self>) -> Result<usize, ()> {
Ok(2)
}
}
#[ntex::test]
async fn test_variant_factory() {
let factory = variant_factory(fn_factory(|| async { Ok::<_, ()>(Srv1) }));
assert!(format!("{factory:?}").contains("Variant"));
let factory = factory
.v2(fn_factory(|| async { Ok::<_, ()>(Srv2) }))
.clone()
.v3(fn_factory(|| async { Ok::<_, ()>(Srv2) }))
.clone();
let service = factory.create(&()).await.unwrap();
assert!(format!("{service:?}").contains("Variant"));
let service = Pipeline::with((), service);
assert!(service.ready().await.is_ok());
service.shutdown().await;
assert_eq!(service.call(Variant3::V1(())).await, Ok(1));
assert_eq!(service.call(Variant3::V2(())).await, Ok(2));
assert_eq!(service.call(Variant3::V3(())).await, Ok(2));
}
#[ntex::test]
async fn test_variant() {
let svc = variant(Srv1).v2(Srv2).clone().v3(Srv2).clone();
assert!(format!("{svc:?}").contains("Variant"));
let svc = Pipeline::with((), svc);
assert!(svc.ready().await.is_ok());
svc.shutdown().await;
assert_eq!(svc.call(Variant3::V1(())).await, Ok(1));
assert_eq!(svc.call(Variant3::V2(())).await, Ok(2));
assert_eq!(svc.call(Variant3::V3(())).await, Ok(2));
}
#[ntex::test]
async fn test_variant_readiness() {
#[derive(Debug, Clone)]
struct Srv5;
impl Service<(), ()> for Srv5 {
type Res = usize;
type Error = ();
async fn ready(&self, _: Ctx<'_, Self>) -> Result<(), Self::Error> {
time::sleep(time::Millis(50)).await;
time::sleep(time::Millis(50)).await;
time::sleep(time::Millis(50)).await;
time::sleep(time::Millis(50)).await;
Ok(())
}
async fn call(&self, _r: (), _: Ctx<'_, Self>) -> Result<usize, ()> {
Ok(2)
}
}
let factory = variant_factory(fn_service(async |()| Ok::<_, ()>(0)))
.v2(fn_factory(async || Ok::<_, ()>(Srv5)).map_init_err(|()| unreachable!()))
.v3(fn_service(async |()| Ok::<_, ()>(2)));
assert!(format!("{factory:?}").contains("Variant"));
let service = factory.clone().create(&()).await.unwrap().clone();
assert!(format!("{service:?}").contains("Variant"));
let service = Pipeline::with((), factory.create(&()).await.unwrap());
assert!(service.ready().await.is_ok());
}
}