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, $(($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            ::std::future::poll_fn(|cx| {
135                let mut ready = Pin::new(&mut fut1).poll(cx)?.is_ready();
136                $(ready = Pin::new(&mut $T).poll(cx)?.is_ready() && ready;)+
137
138                if ready {
139                   Poll::Ready(Ok(()))
140                } else {
141                    Poll::Pending
142                }
143            }).await
144        }
145
146        fn poll(&self, cx: &mut std::task::Context<'_>) -> Result<(), Self::Error> {
147            self.V1.poll(cx)?;
148            $(self.$T.poll(cx)?;)+
149            Ok(())
150        }
151
152        async fn shutdown(&self) {
153            self.V1.shutdown().await;
154            $(self.$T.shutdown().await;)+
155        }
156
157        async fn call(&self, req: $enum_type<V1R, $($R,)+>, ctx: ServiceCtx<'_, Self>) -> Result<Self::Response, Self::Error> {
158            match req {
159                $enum_type::V1(req) => ctx.call(&self.V1, req).await,
160                $($enum_type::$T(req) => ctx.call(&self.$T, req).await,)+
161            }
162        }
163    }
164
165    #[allow(non_snake_case)]
166    pub struct $fac_type<V1, V1C, $($T,)+ V1R, $($R,)+> {
167        V1: V1,
168        $($T: $T,)+
169        _t: PhantomData<(V1C, V1R, $($R,)+)>,
170    }
171
172    impl<V1: Clone, V1C, $($T: Clone,)+ V1R, $($R,)+> Clone for $fac_type<V1, V1C, $($T,)+ V1R, $($R,)+> {
173        fn clone(&self) -> Self {
174            Self {
175                _t: PhantomData,
176                V1: self.V1.clone(),
177                $($T: self.$T.clone(),)+
178            }
179        }
180    }
181
182    impl<V1: fmt::Debug, V1C, $($T: fmt::Debug,)+ V1R, $($R,)+> fmt::Debug for $fac_type<V1, V1C, $($T,)+ V1R, $($R,)+> {
183        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184            f.debug_struct("Variant")
185                .field("V1", &self.V1)
186                $(.field(stringify!($T), &self.$T))+
187                .finish()
188        }
189    }
190
191    impl<V1, V1C, $($T,)+ V1R, $($R,)+> ServiceFactory<$enum_type<V1R, $($R),+>, V1C> for $fac_type<V1, V1C, $($T,)+ V1R, $($R,)+>
192    where
193        V1: ServiceFactory<V1R, V1C>,
194        V1C: Clone,
195        $($T: ServiceFactory< $R, V1C, Response = V1::Response, Error = V1::Error, InitError = V1::InitError>),+
196    {
197        type Response = V1::Response;
198        type Error = V1::Error;
199        type Service = $srv_type<V1::Service, $($T::Service,)+ V1R, $($R,)+>;
200        type InitError = V1::InitError;
201
202        async fn create(&self, cfg: V1C) -> Result<Self::Service, Self::InitError> {
203            Ok($srv_type {
204                V1: self.V1.create(cfg.clone()).await?,
205                $($T: self.$T.create(cfg.clone()).await?,)+
206                _t: PhantomData
207            })
208        }
209    }
210});
211
212#[rustfmt::skip]
213variant_impl!(v2, Variant2, VariantService2, VariantFactory2, (0, V2, V2R));
214#[rustfmt::skip]
215variant_impl!(v3, Variant3, VariantService3, VariantFactory3, (0, V2, V2R), (1, V3, V3R));
216#[rustfmt::skip]
217variant_impl!(v4, Variant4, VariantService4, VariantFactory4, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R));
218#[rustfmt::skip]
219variant_impl!(v5, Variant5, VariantService5, VariantFactory5, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R), (3, V5, V5R));
220#[rustfmt::skip]
221variant_impl!(v6, Variant6, VariantService6, VariantFactory6, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R), (3, V5, V5R), (4, V6, V6R));
222#[rustfmt::skip]
223variant_impl!(v7, Variant7, VariantService7, VariantFactory7, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R), (3, V5, V5R), (4, V6, V6R), (5, V7, V7R));
224#[rustfmt::skip]
225variant_impl!(v8, Variant8, VariantService8, VariantFactory8, (0, V2, V2R), (1, V3, V3R), (2, V4, V4R), (3, V5, V5R), (4, V6, V6R), (5, V7, V7R), (6, V8, V8R));
226
227#[rustfmt::skip]
228variant_impl_and!(VariantFactory2, VariantFactory3, V3, V3R, v3, (V2), (V2R));
229#[rustfmt::skip]
230variant_impl_and!(VariantFactory3, VariantFactory4, V4, V4R, v4, (V2, V3), (V2R, V3R));
231#[rustfmt::skip]
232variant_impl_and!(VariantFactory4, VariantFactory5, V5, V5R, v5, (V2, V3, V4), (V2R, V3R, V4R));
233#[rustfmt::skip]
234variant_impl_and!(VariantFactory5, VariantFactory6, V6, V6R, v6, (V2, V3, V4, V5), (V2R, V3R, V4R, V5R));
235#[rustfmt::skip]
236variant_impl_and!(VariantFactory6, VariantFactory7, V7, V7R, v7, (V2, V3, V4, V5, V6), (V2R, V3R, V4R, V5R, V6R));
237#[rustfmt::skip]
238variant_impl_and!(VariantFactory7, VariantFactory8, V8, V8R, v8, (V2, V3, V4, V5, V6, V7), (V2R, V3R, V4R, V5R, V6R, V7R));
239
240#[cfg(test)]
241mod tests {
242    use ntex_service::fn_factory;
243
244    use super::*;
245
246    #[derive(Debug, Clone)]
247    struct Srv1;
248
249    impl Service<()> for Srv1 {
250        type Response = usize;
251        type Error = ();
252
253        async fn ready(&self, _: ServiceCtx<'_, Self>) -> Result<(), Self::Error> {
254            Ok(())
255        }
256
257        async fn shutdown(&self) {}
258
259        async fn call(&self, _: (), _: ServiceCtx<'_, Self>) -> Result<usize, ()> {
260            Ok(1)
261        }
262    }
263
264    #[derive(Debug, Clone)]
265    struct Srv2;
266
267    impl Service<()> for Srv2 {
268        type Response = usize;
269        type Error = ();
270
271        async fn ready(&self, _: ServiceCtx<'_, Self>) -> Result<(), Self::Error> {
272            Ok(())
273        }
274
275        async fn shutdown(&self) {}
276
277        async fn call(&self, _: (), _: ServiceCtx<'_, Self>) -> Result<usize, ()> {
278            Ok(2)
279        }
280    }
281
282    #[ntex_macros::rt_test2]
283    async fn test_variant() {
284        let factory = variant(fn_factory(|| async { Ok::<_, ()>(Srv1) }));
285        assert!(format!("{factory:?}").contains("Variant"));
286
287        let factory = factory
288            .v2(fn_factory(|| async { Ok::<_, ()>(Srv2) }))
289            .clone()
290            .v3(fn_factory(|| async { Ok::<_, ()>(Srv2) }))
291            .clone();
292
293        let service = factory.pipeline(&()).await.unwrap().clone();
294        assert!(format!("{service:?}").contains("Variant"));
295
296        assert!(crate::future::lazy(|cx| service.poll(cx)).await.is_ok());
297        assert!(service.ready().await.is_ok());
298        service.shutdown().await;
299
300        assert_eq!(service.call(Variant3::V1(())).await, Ok(1));
301        assert_eq!(service.call(Variant3::V2(())).await, Ok(2));
302        assert_eq!(service.call(Variant3::V3(())).await, Ok(2));
303    }
304}