Skip to main content

ntex_service/
fn_service.rs

1use std::{convert::Infallible, fmt, marker::PhantomData};
2
3use crate::{Ctx, IntoService, IntoServiceFactory, Service, ServiceFactory};
4
5/// `Service` implementation for an `AsyncFn(Req) -> Result<Res, Err>` fn.
6#[inline]
7pub fn fn_service<F, Req, Res, Err>(f: F) -> FnService<F, Req, Res, Err>
8where
9    F: AsyncFn(Req) -> Result<Res, Err>,
10{
11    FnService { f, _t: PhantomData }
12}
13
14/// `Service` implementation for an `AsyncFn(Req, &St) -> Result<Res, Err>` function.
15///
16/// This service accesses the pipeline state via the second `&St` parameter.
17#[inline]
18pub fn fn_service_st<F, St, Req, Res, Err>(f: F) -> FnServiceSt<F, St, Req, Res, Err>
19where
20    F: AsyncFn(&St, Req) -> Result<Res, Err>,
21{
22    FnServiceSt { f, _t: PhantomData }
23}
24
25#[inline]
26/// Create `ServiceFactory` for function that can produce services
27///
28/// # Example
29///
30/// ```rust
31/// use std::io;
32/// use ntex_service::{factory, fn_factory, fn_service, Pipeline, Service, ServiceFactory};
33///
34/// /// Service that divides two usize values.
35/// async fn div((x, y): (usize, usize)) -> Result<usize, io::Error> {
36///     if y == 0 {
37///         Err(io::Error::other("divide by zdro"))
38///     } else {
39///         Ok(x / y)
40///     }
41/// }
42///
43/// #[ntex::main]
44/// async fn main() -> io::Result<()> {
45///     // Create service factory that produces `div` services
46///     let fac = fn_factory::<(), _, _, _>(async || {
47///         Ok::<_, io::Error>(fn_service(div))
48///     });
49///
50///     // construct new service
51///     let srv = Pipeline::with((), fac.create(&()).await?);
52///
53///     // now we can use `div` service
54///     let result = srv.call((10, 20)).await?;
55///
56///     println!("10 / 20 = {}", result);
57///
58///     Ok(())
59/// }
60/// ```
61pub fn fn_factory<St, F, Srv, Err>(f: F) -> FnServiceNoConfig<St, F, Srv, Err>
62where
63    F: AsyncFn() -> Result<Srv, Err>,
64{
65    FnServiceNoConfig::new(f)
66}
67
68#[inline]
69/// Create `ServiceFactory` for function that accepts config argument and can produce services
70///
71/// Any function that has following form `AsyncFn(Config) -> Result<Service, Error>` could
72/// act as a `ServiceFactory`.
73///
74/// # Example
75///
76/// ```rust
77/// use std::io;
78/// use ntex_service::{factory_no_st, fn_factory_with_config, fn_service, Pipeline, Service, ServiceFactory};
79///
80/// #[ntex::main]
81/// async fn main() -> io::Result<()> {
82///     // Create service factory. factory uses config argument for
83///     // services it generates.
84///     let fac = fn_factory_with_config(async |y: &usize| {
85///         let y = *y;
86///         Ok::<_, io::Error>(fn_service(move |x: usize| async move { Ok::<_, io::Error>(x * y) }))
87///     });
88///
89///     // construct new service with config argument
90///     let srv = Pipeline::with((), factory_no_st(fac).create(&10).await?);
91///
92///     let result = srv.call(10).await?;
93///     assert_eq!(result, 100);
94///
95///     println!("10 * 10 = {}", result);
96///     Ok(())
97/// }
98/// ```
99pub fn fn_factory_with_config<St, F, Cfg, Srv, Req, Err>(
100    f: F,
101) -> FnServiceConfig<St, F, Cfg, Srv, Req, Err>
102where
103    F: AsyncFn(&Cfg) -> Result<Srv, Err>,
104{
105    FnServiceConfig { f, _t: PhantomData }
106}
107
108/// `Service` implementation for an `AsyncFn(Req) -> Result<Res, Err>` fn.
109pub struct FnService<F, Req, Res, Err> {
110    f: F,
111    _t: PhantomData<(Req, Res, Err)>,
112}
113
114impl<F, Req, Res, Err> Clone for FnService<F, Req, Res, Err>
115where
116    F: Clone,
117{
118    fn clone(&self) -> Self {
119        Self {
120            f: self.f.clone(),
121            _t: PhantomData,
122        }
123    }
124}
125
126impl<F, Req, Res, Err> fmt::Debug for FnService<F, Req, Res, Err> {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        f.debug_struct("FnService")
129            .field("f", &std::any::type_name::<F>())
130            .finish()
131    }
132}
133
134impl<F, St, Req, Res, Err> Service<St, Req> for FnService<F, Req, Res, Err>
135where
136    F: AsyncFn(Req) -> Result<Res, Err>,
137{
138    type Res = Res;
139    type Error = Err;
140
141    #[inline]
142    async fn call(&self, req: Req, _: Ctx<'_, Self, St>) -> Result<Res, Err> {
143        (self.f)(req).await
144    }
145}
146
147impl<F, St, Req, Res, Err, Cfg>
148    IntoServiceFactory<FnServiceFactory<F, Req, Res, Err, Cfg>, St, Req, Cfg>
149    for FnService<F, Req, Res, Err>
150where
151    F: AsyncFn(Req) -> Result<Res, Err> + Clone,
152{
153    #[inline]
154    fn into_factory(self) -> FnServiceFactory<F, Req, Res, Err, Cfg> {
155        FnServiceFactory {
156            f: self.f,
157            _t: PhantomData,
158        }
159    }
160}
161
162impl<F, St, Req, Res, Err> IntoService<FnService<F, Req, Res, Err>, St, Req> for F
163where
164    F: AsyncFn(Req) -> Result<Res, Err>,
165{
166    #[inline]
167    fn into_service(self) -> FnService<F, Req, Res, Err> {
168        FnService {
169            f: self,
170            _t: PhantomData,
171        }
172    }
173}
174
175/// `Service` implementation for an `AsyncFn(Req, &St) -> Result<Res, Err>` function.
176///
177/// This service accesses the pipeline state via the second `&St` parameter.
178pub struct FnServiceSt<F, St, Req, Res, Err> {
179    f: F,
180    _t: PhantomData<(St, Req, Res, Err)>,
181}
182
183impl<F, St, Req, Res, Err> Clone for FnServiceSt<F, St, Req, Res, Err>
184where
185    F: Clone,
186{
187    fn clone(&self) -> Self {
188        Self {
189            f: self.f.clone(),
190            _t: PhantomData,
191        }
192    }
193}
194
195impl<F, St, Req, Res, Err> fmt::Debug for FnServiceSt<F, St, Req, Res, Err> {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        f.debug_struct("FnServiceSt")
198            .field("f", &std::any::type_name::<F>())
199            .finish()
200    }
201}
202
203impl<F, St, Req, Res, Err> Service<St, Req> for FnServiceSt<F, St, Req, Res, Err>
204where
205    F: AsyncFn(&St, Req) -> Result<Res, Err>,
206{
207    type Res = Res;
208    type Error = Err;
209
210    #[inline]
211    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<Res, Err> {
212        (self.f)(ctx.st(), req).await
213    }
214}
215
216impl<F, St, Req, Res, Err, Cfg>
217    IntoServiceFactory<FnServiceStFactory<F, St, Req, Res, Err>, St, Req, Cfg>
218    for FnServiceSt<F, St, Req, Res, Err>
219where
220    F: AsyncFn(&St, Req) -> Result<Res, Err> + Clone,
221{
222    #[inline]
223    fn into_factory(self) -> FnServiceStFactory<F, St, Req, Res, Err> {
224        FnServiceStFactory {
225            f: self.f,
226            ph: PhantomData,
227        }
228    }
229}
230
231impl<F, St, Req, Res, Err> IntoService<FnServiceSt<F, St, Req, Res, Err>, St, Req> for F
232where
233    F: AsyncFn(&St, Req) -> Result<Res, Err>,
234{
235    #[inline]
236    fn into_service(self) -> FnServiceSt<F, St, Req, Res, Err> {
237        FnServiceSt {
238            f: self,
239            _t: PhantomData,
240        }
241    }
242}
243
244// ---------------------------- FnServiceFactory ------------------------
245
246pub struct FnServiceFactory<F, Req, Res, Err, Cfg>
247where
248    F: AsyncFn(Req) -> Result<Res, Err> + Clone,
249{
250    f: F,
251    _t: PhantomData<(Req, Cfg)>,
252}
253
254impl<F, Req, Res, Err, Cfg> FnServiceFactory<F, Req, Res, Err, Cfg>
255where
256    F: AsyncFn(Req) -> Result<Res, Err> + Clone,
257{
258    fn new(f: F) -> Self {
259        FnServiceFactory { f, _t: PhantomData }
260    }
261}
262
263impl<F, Req, Res, Err, Cfg> Clone for FnServiceFactory<F, Req, Res, Err, Cfg>
264where
265    F: AsyncFn(Req) -> Result<Res, Err> + Clone,
266{
267    #[inline]
268    fn clone(&self) -> Self {
269        Self {
270            f: self.f.clone(),
271            _t: PhantomData,
272        }
273    }
274}
275
276impl<F, Req, Res, Err, Cfg> fmt::Debug for FnServiceFactory<F, Req, Res, Err, Cfg>
277where
278    F: AsyncFn(Req) -> Result<Res, Err> + Clone,
279{
280    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281        f.debug_struct("FnServiceFactory")
282            .field("f", &std::any::type_name::<F>())
283            .finish()
284    }
285}
286
287impl<F, St, Req, Res, Err, Cfg> ServiceFactory<St, Req, Cfg>
288    for FnServiceFactory<F, Req, Res, Err, Cfg>
289where
290    F: AsyncFn(Req) -> Result<Res, Err> + Clone,
291{
292    type Res = Res;
293    type Error = Err;
294
295    type Service = FnService<F, Req, Res, Err>;
296    type InitError = Infallible;
297
298    #[inline]
299    async fn create(&self, _: &Cfg) -> Result<Self::Service, Self::InitError> {
300        Ok(FnService {
301            f: self.f.clone(),
302            _t: PhantomData,
303        })
304    }
305}
306
307impl<St, F, Req, Res, Err, Cfg>
308    IntoServiceFactory<FnServiceFactory<F, Req, Res, Err, Cfg>, St, Req, Cfg> for F
309where
310    F: AsyncFn(Req) -> Result<Res, Err> + Clone,
311{
312    #[inline]
313    fn into_factory(self) -> FnServiceFactory<F, Req, Res, Err, Cfg> {
314        FnServiceFactory::new(self)
315    }
316}
317
318// ========================= FnServiceStFactory =======================
319
320pub struct FnServiceStFactory<F, St, Req, Res, Err>
321where
322    F: AsyncFn(&St, Req) -> Result<Res, Err> + Clone,
323{
324    f: F,
325    ph: PhantomData<(St, Req, Res, Err)>,
326}
327
328impl<F, St, Req, Res, Err> FnServiceStFactory<F, St, Req, Res, Err>
329where
330    F: AsyncFn(&St, Req) -> Result<Res, Err> + Clone,
331{
332    fn new(f: F) -> Self {
333        FnServiceStFactory { f, ph: PhantomData }
334    }
335}
336
337impl<F, St, Req, Res, Err> Clone for FnServiceStFactory<F, St, Req, Res, Err>
338where
339    F: AsyncFn(&St, Req) -> Result<Res, Err> + Clone,
340{
341    #[inline]
342    fn clone(&self) -> Self {
343        Self {
344            f: self.f.clone(),
345            ph: PhantomData,
346        }
347    }
348}
349
350impl<F, St, Req, Res, Err> fmt::Debug for FnServiceStFactory<F, St, Req, Res, Err>
351where
352    F: AsyncFn(&St, Req) -> Result<Res, Err> + Clone,
353{
354    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355        f.debug_struct("FnServiceStFactory")
356            .field("f", &std::any::type_name::<F>())
357            .finish()
358    }
359}
360
361impl<F, St, Req, Res, Err, Cfg> ServiceFactory<St, Req, Cfg>
362    for FnServiceStFactory<F, St, Req, Res, Err>
363where
364    F: AsyncFn(&St, Req) -> Result<Res, Err> + Clone,
365{
366    type Res = Res;
367    type Error = Err;
368
369    type Service = FnServiceSt<F, St, Req, Res, Err>;
370    type InitError = Infallible;
371
372    #[inline]
373    async fn create(&self, _: &Cfg) -> Result<Self::Service, Self::InitError> {
374        Ok(FnServiceSt {
375            f: self.f.clone(),
376            _t: PhantomData,
377        })
378    }
379}
380
381impl<F, St, Req, Res, Err, Cfg>
382    IntoServiceFactory<FnServiceStFactory<F, St, Req, Res, Err>, St, Req, Cfg> for F
383where
384    F: AsyncFn(&St, Req) -> Result<Res, Err> + Clone,
385{
386    #[inline]
387    fn into_factory(self) -> FnServiceStFactory<F, St, Req, Res, Err> {
388        FnServiceStFactory::new(self)
389    }
390}
391
392// ========================= FnServiceConfig ==================================
393
394/// `ServiceFactory` for a `AsyncFn(Cfg) -> Result<Srv, Err>` function
395pub struct FnServiceConfig<St, F, Cfg, Srv, Req, Err>
396where
397    F: AsyncFn(&Cfg) -> Result<Srv, Err>,
398{
399    f: F,
400    _t: PhantomData<(St, Cfg, Srv, Req, Err)>,
401}
402
403impl<St, F, Cfg, Srv, Req, Err> Clone for FnServiceConfig<St, F, Cfg, Srv, Req, Err>
404where
405    F: AsyncFn(&Cfg) -> Result<Srv, Err> + Clone,
406{
407    #[inline]
408    fn clone(&self) -> Self {
409        FnServiceConfig {
410            f: self.f.clone(),
411            _t: PhantomData,
412        }
413    }
414}
415
416impl<St, F, Cfg, Srv, Req, Err> fmt::Debug for FnServiceConfig<St, F, Cfg, Srv, Req, Err>
417where
418    F: AsyncFn(&Cfg) -> Result<Srv, Err>,
419{
420    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421        f.debug_struct("FnServiceConfig")
422            .field("f", &std::any::type_name::<F>())
423            .finish()
424    }
425}
426
427impl<St, F, Cfg, S, Req, Err> ServiceFactory<St, Req, Cfg>
428    for FnServiceConfig<St, F, Cfg, S, Req, Err>
429where
430    F: AsyncFn(&Cfg) -> Result<S, Err>,
431    S: Service<St, Req>,
432{
433    type Res = S::Res;
434    type Error = S::Error;
435
436    type Service = S;
437    type InitError = Err;
438
439    #[inline]
440    async fn create(&self, cfg: &Cfg) -> Result<Self::Service, Self::InitError> {
441        (self.f)(cfg).await
442    }
443}
444
445impl<F, St, Cfg, S, Req, Err>
446    IntoServiceFactory<FnServiceConfig<St, F, Cfg, S, Req, Err>, St, Req, Cfg> for F
447where
448    F: AsyncFn(&Cfg) -> Result<S, Err> + Clone,
449    S: Service<St, Req>,
450{
451    #[inline]
452    fn into_factory(self) -> FnServiceConfig<St, F, Cfg, S, Req, Err> {
453        FnServiceConfig {
454            f: self,
455            _t: PhantomData,
456        }
457    }
458}
459
460// ====================== FnServiceNoConfig =========================
461
462/// `ServiceFactory` for a `Fn() -> Future<Service>` function
463pub struct FnServiceNoConfig<St, F, S, E, C = ()>
464where
465    F: AsyncFn() -> Result<S, E>,
466{
467    f: F,
468    _t: PhantomData<(St, C)>,
469}
470
471impl<St, F, S, E, C> FnServiceNoConfig<St, F, S, E, C>
472where
473    F: AsyncFn() -> Result<S, E>,
474{
475    fn new(f: F) -> Self {
476        Self { f, _t: PhantomData }
477    }
478}
479
480impl<St, F, S, Req, E, C> ServiceFactory<St, Req, C> for FnServiceNoConfig<St, F, S, E, C>
481where
482    F: AsyncFn() -> Result<S, E>,
483    S: Service<St, Req>,
484    C: 'static,
485{
486    type Res = S::Res;
487    type Error = S::Error;
488    type Service = S;
489    type InitError = E;
490
491    #[inline]
492    async fn create(&self, _: &C) -> Result<S, E> {
493        (self.f)().await
494    }
495}
496
497impl<St, F, S, E, C> Clone for FnServiceNoConfig<St, F, S, E, C>
498where
499    F: AsyncFn() -> Result<S, E> + Clone,
500{
501    #[inline]
502    fn clone(&self) -> Self {
503        Self::new(self.f.clone())
504    }
505}
506
507impl<St, F, S, E, C> fmt::Debug for FnServiceNoConfig<St, F, S, E, C>
508where
509    F: AsyncFn() -> Result<S, E>,
510{
511    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512        f.debug_struct("FnServiceNoConfig")
513            .field("f", &std::any::type_name::<F>())
514            .finish()
515    }
516}
517
518#[cfg(test)]
519mod tests {
520    use ntex::util::lazy;
521    use std::task::Poll;
522
523    use super::*;
524    use crate::{Pipeline, factory_no_st};
525
526    #[ntex::test]
527    async fn test_fn_service() {
528        let new_srv = factory_no_st(fn_service(async |()| Ok::<_, ()>("srv")).clone());
529        let _ = format!("{new_srv:?}");
530
531        let srv = Pipeline::with((), new_srv.create(&()).await.unwrap());
532        let res = srv.call(()).await;
533        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
534        assert!(res.is_ok());
535        assert_eq!(res.unwrap(), "srv");
536        let _ = format!("{srv:?}");
537
538        let new_srv = fn_service(async |()| Ok::<_, ()>("srv"));
539        let srv = Pipeline::with((), new_srv.clone());
540        let res = srv.call(()).await;
541        assert!(res.is_ok());
542        assert_eq!(res.unwrap(), "srv");
543        let _ = format!("{srv:?}");
544
545        assert_eq!(lazy(|cx| srv.poll_shutdown(cx)).await, Poll::Ready(()));
546    }
547
548    #[ntex::test]
549    async fn test_fn_service_comp() {
550        let new_srv = fn_service(async |()| Ok::<_, ()>("srv")).clone();
551        let _ = format!("{new_srv:?}");
552
553        let srv = Pipeline::with((), factory_no_st(new_srv).create(&()).await.unwrap());
554        let res = srv.call(()).await;
555        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
556        assert!(res.is_ok());
557        assert_eq!(res.unwrap(), "srv");
558        let _ = format!("{srv:?}");
559
560        let new_srv = fn_service(async |()| Ok::<_, ()>("srv")).clone();
561        let srv = Pipeline::with((), new_srv.clone());
562        let res = srv.call(()).await;
563        assert!(res.is_ok());
564        assert_eq!(res.unwrap(), "srv");
565        let _ = format!("{srv:?}");
566
567        assert_eq!(lazy(|cx| srv.poll_shutdown(cx)).await, Poll::Ready(()));
568    }
569
570    #[ntex::test]
571    async fn test_fn_service_service() {
572        let srv = Pipeline::with(
573            (),
574            factory_no_st(fn_service(async |()| Ok::<_, ()>("srv")).clone())
575                .create(&())
576                .await
577                .unwrap()
578                .clone(),
579        );
580
581        let res = srv.call(()).await;
582        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
583        assert!(res.is_ok());
584        assert_eq!(res.unwrap(), "srv");
585        assert_eq!(lazy(|cx| srv.poll_shutdown(cx)).await, Poll::Ready(()));
586    }
587
588    #[ntex::test]
589    async fn test_fn_service_with_config() {
590        let new_srv = factory_no_st(fn_factory_with_config(async move |cfg: &usize| {
591            let cfg = *cfg;
592            Ok::<_, ()>(fn_service(async move |()| Ok::<_, ()>(("srv", cfg))))
593        }))
594        .clone();
595
596        let srv = Pipeline::with((), new_srv.create(&1).await.unwrap());
597        let res = srv.call(()).await;
598        assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
599        assert!(res.is_ok());
600        assert_eq!(res.unwrap(), ("srv", 1));
601    }
602}