Skip to main content

ntex_service/
fn_ready.rs

1use std::{convert::Infallible, fmt, marker::PhantomData};
2
3use crate::{Ctx, Service, ServiceFactory};
4
5/// A pass-through service with readiness controlled by an asynchronous function.
6pub struct FnReadiness<F, Err> {
7    f: F,
8    err: PhantomData<Err>,
9}
10
11impl<F, Err> FnReadiness<F, Err> {
12    /// Creates a readiness service from an asynchronous function.
13    pub fn new<St>(f: F) -> Self
14    where
15        F: AsyncFn(&St) -> Result<(), Err>,
16    {
17        Self {
18            f,
19            err: PhantomData,
20        }
21    }
22}
23
24impl<F, Err> Clone for FnReadiness<F, Err>
25where
26    F: Clone,
27{
28    #[inline]
29    fn clone(&self) -> Self {
30        Self {
31            f: self.f.clone(),
32            err: PhantomData,
33        }
34    }
35}
36
37impl<F, Err> fmt::Debug for FnReadiness<F, Err> {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        f.debug_struct("FnReadiness")
40            .field("fn", &std::any::type_name::<F>())
41            .finish()
42    }
43}
44
45impl<F, Err, St, Req> ServiceFactory<St, Req> for FnReadiness<F, Err>
46where
47    F: AsyncFn(&St) -> Result<(), Err> + Clone,
48{
49    type Res = Req;
50    type Error = Err;
51
52    type Service = FnReadiness<F, Err>;
53    type InitError = Infallible;
54
55    #[inline]
56    async fn create(&self, _: &St) -> Result<Self::Service, Self::InitError> {
57        Ok(self.clone())
58    }
59}
60
61impl<F, St, Req, Err> Service<St, Req> for FnReadiness<F, Err>
62where
63    F: AsyncFn(&St) -> Result<(), Err> + Clone,
64{
65    type Res = Req;
66    type Error = Err;
67
68    #[inline]
69    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), Err> {
70        (self.f)(ctx.st()).await
71    }
72
73    #[inline]
74    async fn call(&self, req: Req, _: Ctx<'_, Self, St>) -> Result<Req, Err> {
75        Ok(req)
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use std::{cell::Cell, rc::Rc};
82
83    use crate::{Pipeline, factory, service};
84
85    use super::*;
86
87    #[ntex::test]
88    async fn test_fn_readiness() {
89        // Service
90        let is_called = Rc::new(Cell::new(false));
91        let is_called2 = is_called.clone();
92
93        let svc = service(async |()| Ok::<_, ()>("pipe")).readiness(async move |()| {
94            is_called2.set(true);
95            Ok(())
96        });
97        let _ = format!("{svc:?}");
98        let pipe = Pipeline::new((), svc);
99
100        let res = pipe.call(()).await;
101        assert_eq!(pipe.ready().await, Ok(()));
102        assert!(res.is_ok());
103        assert_eq!(res.unwrap(), "pipe");
104        assert!(is_called.get());
105
106        // Service factory
107        let is_called = Rc::new(Cell::new(false));
108        let is_called2 = is_called.clone();
109
110        let factory = factory(|()| async { Ok::<_, ()>("pipe") }).readiness(async move |()| {
111            is_called2.set(true);
112            Ok(())
113        });
114        let pipe = Pipeline::new((), factory.create(&()).await.unwrap());
115
116        let res = pipe.call(()).await;
117        assert_eq!(pipe.ready().await, Ok(()));
118        assert!(res.is_ok());
119        assert_eq!(res.unwrap(), "pipe");
120        assert!(is_called.get());
121    }
122}