ntex_util/services/
variant.rs

1//! Contains `Variant` service and related types and functions.
2#![allow(non_snake_case)]
3use std::{fmt, marker::PhantomData, task::Poll};
4
5use ntex_service::{IntoServiceFactory, Service, ServiceCtx, ServiceFactory};
6
7/// Construct `Variant` service factory.
8///
9/// Variant service allow to combine multiple different services into a single service.
10pub fn variant<V1: ServiceFactory<V1R, V1C>, V1R, V1C>(
11    factory: V1,
12) -> Variant<V1, V1R, V1C> {
13    Variant {
14        factory,
15        _t: PhantomData,
16    }
17}
18
19/// Combine multiple different service types into a single service.
20pub struct Variant<A, AR, AC> {
21    factory: A,
22    _t: PhantomData<(AR, AC)>,
23}
24
25impl<A, AR, AC> Variant<A, AR, AC>
26where
27    A: ServiceFactory<AR, AC>,
28    AC: Clone,
29{
30    /// Convert to a Variant with two request types
31    pub fn v2<B, BR, F>(self, factory: F) -> VariantFactory2<A, AC, B, AR, BR>
32    where
33        B: ServiceFactory<
34            BR,
35            AC,
36            Response = A::Response,
37            Error = A::Error,
38            InitError = A::InitError,
39        >,
40        F: IntoServiceFactory<B, BR, AC>,
41    {
42        VariantFactory2 {
43            V1: self.factory,
44            V2: factory.into_factory(),
45            _t: PhantomData,
46        }
47    }
48}
49
50impl<A, AR, AC> fmt::Debug for Variant<A, AR, AC>
51where
52    A: fmt::Debug,
53{
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        f.debug_struct("Variant")
56            .field("V1", &self.factory)
57            .finish()
58    }
59}
60
61macro_rules! variant_impl_and ({$fac1_type:ident, $fac2_type:ident, $name:ident, $r_name:ident, $m_name:ident, ($($T:ident),+), ($($R:ident),+)} => {
62
63    #[allow(non_snake_case)]
64    impl<V1, V1C, $($T,)+ V1R, $($R,)+> $fac1_type<V1, V1C, $($T,)+ V1R, $($R,)+>
65        where
66            V1: ServiceFactory<V1R, V1C>,
67        {
68            /// Convert to a Variant with more request types
69            pub fn $m_name<$name, $r_name, F>(self, factory: F) -> $fac2_type<V1, V1C, $($T,)+ $name, V1R, $($R,)+ $r_name>
70            where $name: ServiceFactory<$r_name, V1C,
71                    Response = V1::Response,
72                    Error = V1::Error,
73                    InitError = V1::InitError>,
74                  F: IntoServiceFactory<$name, $r_name, V1C>,
75            {
76                $fac2_type {
77                    V1: self.V1,
78                    $($T: self.$T,)+
79                    $name: factory.into_factory(),
80                    _t: PhantomData
81                }
82            }
83    }
84});
85
86macro_rules! variant_impl ({$mod_name:ident, $enum_type:ident, $srv_type:ident, $fac_type:ident, $num:literal, $(($n:tt, $T:ident, $R:ident)),+} => {
87
88    #[allow(non_snake_case, missing_debug_implementations)]
89    pub enum $enum_type<V1R, $($R),+> {
90        V1(V1R),
91        $($T($R),)+
92    }
93
94    #[allow(non_snake_case)]
95    pub struct $srv_type<V1, $($T,)+ V1R, $($R,)+> {
96        V1: V1,
97        $($T: $T,)+
98        _t: PhantomData<(V1R, $($R),+)>,
99    }
100
101    impl<V1: Clone, $($T: Clone,)+ V1R, $($R,)+> Clone for $srv_type<V1, $($T,)+ V1R, $($R,)+> {
102        fn clone(&self) -> Self {
103            Self {
104                _t: PhantomData,
105                V1: self.V1.clone(),
106                $($T: self.$T.clone(),)+
107            }
108        }
109    }
110
111    impl<V1: fmt::Debug, $($T: fmt::Debug,)+ V1R, $($R,)+> fmt::Debug for $srv_type<V1, $($T,)+ V1R, $($R,)+> {
112        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113            f.debug_struct(stringify!($srv_type))
114                .field("V1", &self.V1)
115                $(.field(stringify!($T), &self.$T))+
116                .finish()
117        }
118    }
119
120    impl<V1, $($T,)+ V1R, $($R,)+> Service<$enum_type<V1R, $($R,)+>> for $srv_type<V1, $($T,)+ V1R, $($R,)+>
121    where
122        V1: Service<V1R>,
123        $($T: Service<$R, Response = V1::Response, Error = V1::Error>),+
124    {
125        type Response = V1::Response;
126        type Error = V1::Error;
127
128        async fn ready(&self, ctx: ServiceCtx<'_, Self>) -> Result<(), Self::Error> {
129            use std::{future::Future, pin::Pin};
130
131            let mut fut1 = ::std::pin::pin!(ctx.ready(&self.V1));
132            $(let mut $T = ::std::pin::pin!(ctx.ready(&self.$T));)+
133
134            let mut ready: [bool; $num] = [false; $num];
135
136            ::std::future::poll_fn(|cx| {
137                if !ready[$num-1] {
138                    ready[$num-1] = Pin::new(&mut fut1).poll(cx)?.is_ready();
139                }
140                $(if !ready[$n] {
141                    ready[$n] = Pin::new(&mut $T).poll(cx)?.is_ready();
142                })+;
143
144                for v in &ready[..] {
145                    if !v {
146                        return Poll::Pending
147                    }
148                }
149                Poll::Ready(Ok(()))
150            }).await
151        }
152
153        fn poll(&self, cx: &mut std::task::Context<'_>) -> Result<(), Self::Error> {
154            self.V1.poll(cx)?;
155            $(self.$T.poll(cx)?;)+
156            Ok(())
157        }
158
159        async fn shutdown(&self) {
160            self.V1.shutdown().await;
161            $(self.$T.shutdown().await;)+
162        }
163
164        async fn call(&self, req: $enum_type<V1R, $($R,)+>, ctx: ServiceCtx<'_, Self>) -> Result<Self::Response, Self::Error> {
165            match req {
166                $enum_type::V1(req) => ctx.call(&self.V1, req).await,
167                $($enum_type::$T(req) => ctx.call(&self.$T, req).await,)+
168            }
169        }
170    }
171
172    #[allow(non_snake_case)]
173    pub struct $fac_type<V1, V1C, $($T,)+ V1R, $($R,)+> {
174        V1: V1,
175        $($T: $T,)+
176        _t: PhantomData<(V1C, V1R, $($R,)+)>,
177    }
178
179    impl<V1: Clone, V1C, $($T: Clone,)+ V1R, $($R,)+> Clone for $fac_type<V1, V1C, $($T,)+ V1R, $($R,)+> {
180        fn clone(&self) -> Self {
181            Self {
182                _t: PhantomData,
183                V1: self.V1.clone(),
184                $($T: self.$T.clone(),)+
185            }
186        }
187    }
188
189    impl<V1: fmt::Debug, V1C, $($T: fmt::Debug,)+ V1R, $($R,)+> fmt::Debug for $fac_type<V1, V1C, $($T,)+ V1R, $($R,)+> {
190        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191            f.debug_struct("Variant")
192                .field("V1", &self.V1)
193                $(.field(stringify!($T), &self.$T))+
194                .finish()
195        }
196    }
197
198    impl<V1, V1C, $($T,)+ V1R, $($R,)+> ServiceFactory<$enum_type<V1R, $($R),+>, V1C> for $fac_type<V1, V1C, $($T,)+ V1R, $($R,)+>
199    where
200        V1: ServiceFactory<V1R, V1C>,
201        V1C: Clone,
202        $($T: ServiceFactory< $R, V1C, Response = V1::Response, Error = V1::Error, InitError = V1::InitError>),+
203    {
204        type Response = V1::Response;
205        type Error = V1::Error;
206        type Service = $srv_type<V1::Service, $($T::Service,)+ V1R, $($R,)+>;
207        type InitError = V1::InitError;
208
209        async fn create(&self, cfg: V1C) -> Result<Self::Service, Self::InitError> {
210            Ok($srv_type {
211                V1: self.V1.create(cfg.clone()).await?,
212                $($T: self.$T.create(cfg.clone()).await?,)+
213                _t: PhantomData
214            })
215        }
216    }
217});
218
219#[rustfmt::skip]
220variant_impl!(v2, Variant2, VariantService2, VariantFactory2, 2, (0, V2, V2R));
221#[rustfmt::skip]
222variant_impl!(v3, Variant3, VariantService3, VariantFactory3, 3, (0, V2, V2R), (1, V3, V3R));
223#[rustfmt::skip]
224variant_impl!(v4, Variant4, VariantService4, VariantFactory4, 4, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R));
225#[rustfmt::skip]
226variant_impl!(v5, Variant5, VariantService5, VariantFactory5, 5, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R), (3, V5, V5R));
227#[rustfmt::skip]
228variant_impl!(v6, Variant6, VariantService6, VariantFactory6, 6, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R), (3, V5, V5R), (4, V6, V6R));
229#[rustfmt::skip]
230variant_impl!(v7, Variant7, VariantService7, VariantFactory7, 7, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R), (3, V5, V5R), (4, V6, V6R), (5, V7, V7R));
231#[rustfmt::skip]
232variant_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));
233
234#[rustfmt::skip]
235variant_impl_and!(VariantFactory2, VariantFactory3, V3, V3R, v3, (V2), (V2R));
236#[rustfmt::skip]
237variant_impl_and!(VariantFactory3, VariantFactory4, V4, V4R, v4, (V2, V3), (V2R, V3R));
238#[rustfmt::skip]
239variant_impl_and!(VariantFactory4, VariantFactory5, V5, V5R, v5, (V2, V3, V4), (V2R, V3R, V4R));
240#[rustfmt::skip]
241variant_impl_and!(VariantFactory5, VariantFactory6, V6, V6R, v6, (V2, V3, V4, V5), (V2R, V3R, V4R, V5R));
242#[rustfmt::skip]
243variant_impl_and!(VariantFactory6, VariantFactory7, V7, V7R, v7, (V2, V3, V4, V5, V6), (V2R, V3R, V4R, V5R, V6R));
244#[rustfmt::skip]
245variant_impl_and!(VariantFactory7, VariantFactory8, V8, V8R, v8, (V2, V3, V4, V5, V6, V7), (V2R, V3R, V4R, V5R, V6R, V7R));
246
247#[cfg(test)]
248mod tests {
249    use ntex_service::{fn_factory, fn_service};
250
251    use super::*;
252    use crate::time;
253
254    #[derive(Debug, Clone)]
255    struct Srv1;
256
257    impl Service<()> for Srv1 {
258        type Response = usize;
259        type Error = ();
260
261        async fn ready(&self, _: ServiceCtx<'_, Self>) -> Result<(), Self::Error> {
262            Ok(())
263        }
264
265        async fn shutdown(&self) {}
266
267        async fn call(&self, _: (), _: ServiceCtx<'_, Self>) -> Result<usize, ()> {
268            Ok(1)
269        }
270    }
271
272    #[derive(Debug, Clone)]
273    struct Srv2;
274
275    impl Service<()> for Srv2 {
276        type Response = usize;
277        type Error = ();
278
279        async fn ready(&self, _: ServiceCtx<'_, Self>) -> Result<(), Self::Error> {
280            Ok(())
281        }
282
283        async fn shutdown(&self) {}
284
285        async fn call(&self, _: (), _: ServiceCtx<'_, Self>) -> Result<usize, ()> {
286            Ok(2)
287        }
288    }
289
290    #[ntex_macros::rt_test2]
291    async fn test_variant() {
292        let factory = variant(fn_factory(|| async { Ok::<_, ()>(Srv1) }));
293        assert!(format!("{factory:?}").contains("Variant"));
294
295        let factory = factory
296            .v2(fn_factory(|| async { Ok::<_, ()>(Srv2) }))
297            .clone()
298            .v3(fn_factory(|| async { Ok::<_, ()>(Srv2) }))
299            .clone();
300
301        let service = factory.pipeline(&()).await.unwrap().clone();
302        assert!(format!("{service:?}").contains("Variant"));
303
304        assert!(crate::future::lazy(|cx| service.poll(cx)).await.is_ok());
305        assert!(service.ready().await.is_ok());
306        service.shutdown().await;
307
308        assert_eq!(service.call(Variant3::V1(())).await, Ok(1));
309        assert_eq!(service.call(Variant3::V2(())).await, Ok(2));
310        assert_eq!(service.call(Variant3::V3(())).await, Ok(2));
311    }
312
313    #[ntex_macros::rt_test2]
314    async fn test_variant_readiness() {
315        #[derive(Debug, Clone)]
316        struct Srv5;
317
318        impl Service<()> for Srv5 {
319            type Response = usize;
320            type Error = ();
321            async fn ready(&self, _: ServiceCtx<'_, Self>) -> Result<(), Self::Error> {
322                time::sleep(time::Millis(50)).await;
323                time::sleep(time::Millis(50)).await;
324                time::sleep(time::Millis(50)).await;
325                time::sleep(time::Millis(50)).await;
326                Ok(())
327            }
328            async fn shutdown(&self) {}
329            async fn call(&self, _: (), _: ServiceCtx<'_, Self>) -> Result<usize, ()> {
330                Ok(2)
331            }
332        }
333
334        let factory = variant(fn_service(|()| async { Ok::<_, ()>(0) }))
335            .v2(fn_factory(|| async { Ok::<_, ()>(Srv5) }))
336            .v3(fn_service(|()| crate::future::Ready::Ok::<_, ()>(2)));
337        let service = factory.pipeline(&()).await.unwrap().clone();
338        assert!(service.ready().await.is_ok());
339    }
340}