xitca_service/service/
ext.rs

1use crate::{
2    middleware::{AsyncFn, EnclosedBuilder, EnclosedFnBuilder, Map, MapBuilder, MapErr, MapErrorBuilder},
3    pipeline::{PipelineT, marker},
4};
5
6use super::Service;
7
8/// extend trait for [Service] providing combinator functionalities.
9pub trait ServiceExt<Arg>: Service<Arg> {
10    /// Enclose Self with given `T as Service<<Self as Service<_>>::Response>>`. In other word T
11    /// would take Self's `Service::Response` type as it's generic argument of `Service<_>` impl.
12    fn enclosed<T>(self, build: T) -> EnclosedBuilder<Self, T>
13    where
14        T: Service<Result<Self::Response, Self::Error>>,
15        Self: Sized,
16    {
17        PipelineT::new(self, build)
18    }
19
20    /// Function version of [Self::enclosed] method.
21    fn enclosed_fn<T, Req, O>(self, func: T) -> EnclosedFnBuilder<Self, T>
22    where
23        T: core::ops::AsyncFn(&Self::Response, Req) -> O + Clone,
24        Self: Sized,
25    {
26        self.enclosed(AsyncFn(func))
27    }
28
29    /// Mutate `<<Self::Response as Service<Req>>::Future as Future>::Output` type with given
30    /// closure.
31    fn map<F, Res, ResMap>(self, mapper: F) -> MapBuilder<Self, F>
32    where
33        F: Fn(Res) -> ResMap + Clone,
34        Self: Sized,
35    {
36        self.enclosed(Map(mapper))
37    }
38
39    /// Mutate `<Self::Response as Service<Req>>::Error` type with given closure.
40    fn map_err<F, Err, ErrMap>(self, err: F) -> MapErrorBuilder<Self, F>
41    where
42        F: Fn(Err) -> ErrMap + Clone,
43        Self: Sized,
44    {
45        self.enclosed(MapErr(err))
46    }
47
48    /// Chain another service factory who's service takes `Self`'s `Service::Response` output as
49    /// `Service::Request`.
50    fn and_then<F>(self, factory: F) -> PipelineT<Self, F, marker::BuildAndThen>
51    where
52        F: Service<Arg>,
53        Self: Sized,
54    {
55        PipelineT::new(self, factory)
56    }
57}
58
59impl<S, Arg> ServiceExt<Arg> for S where S: Service<Arg> {}
60
61#[cfg(test)]
62mod test {
63    use xitca_unsafe_collection::futures::NowOrPanic;
64
65    use crate::fn_service;
66
67    use super::*;
68
69    #[derive(Clone)]
70    struct DummyMiddleware;
71
72    struct DummyMiddlewareService<S>(S);
73
74    impl<S, E> Service<Result<S, E>> for DummyMiddleware {
75        type Response = DummyMiddlewareService<S>;
76        type Error = E;
77
78        async fn call(&self, res: Result<S, E>) -> Result<Self::Response, Self::Error> {
79            res.map(DummyMiddlewareService)
80        }
81    }
82
83    impl<S, Req> Service<Req> for DummyMiddlewareService<S>
84    where
85        S: Service<Req>,
86    {
87        type Response = S::Response;
88        type Error = S::Error;
89
90        async fn call(&self, req: Req) -> Result<Self::Response, Self::Error> {
91            self.0.call(req).await
92        }
93    }
94
95    async fn index(s: &'static str) -> Result<&'static str, ()> {
96        Ok(s)
97    }
98
99    #[cfg(feature = "alloc")]
100    #[test]
101    fn service_object() {
102        let service = fn_service(index)
103            .enclosed(DummyMiddleware)
104            .call(())
105            .now_or_panic()
106            .unwrap();
107
108        let res = service.call("996").now_or_panic().unwrap();
109        assert_eq!(res, "996");
110    }
111
112    #[test]
113    fn map() {
114        let service = fn_service(index).map(|_| "251").call(()).now_or_panic().unwrap();
115
116        let err = service.call("996").now_or_panic().ok().unwrap();
117        assert_eq!(err, "251");
118    }
119
120    #[test]
121    fn map_err() {
122        let service = fn_service(|_: &str| async { Err::<(), _>(()) })
123            .map_err(|_| "251")
124            .call(())
125            .now_or_panic()
126            .unwrap();
127
128        let err = service.call("996").now_or_panic().err().unwrap();
129        assert_eq!(err, "251");
130    }
131
132    #[test]
133    fn enclosed_fn() {
134        async fn enclosed<S>(service: &S, req: &'static str) -> Result<&'static str, ()>
135        where
136            S: Service<&'static str, Response = &'static str, Error = ()>,
137        {
138            let res = service.call(req).await?;
139            assert_eq!(res, "996");
140            Ok("251")
141        }
142
143        let res = fn_service(index)
144            .enclosed_fn(enclosed)
145            .enclosed_fn(async |service, req| service.call(req).await)
146            .call(())
147            .now_or_panic()
148            .unwrap()
149            .call("996")
150            .now_or_panic()
151            .ok()
152            .unwrap();
153
154        assert_eq!(res, "251");
155    }
156
157    #[cfg(feature = "alloc")]
158    #[test]
159    fn enclosed_opt() {
160        let service = fn_service(index)
161            .enclosed(Some(DummyMiddleware))
162            .call(())
163            .now_or_panic()
164            .unwrap();
165
166        let res = service.call("996").now_or_panic().unwrap();
167        assert_eq!(res, "996");
168
169        let service = fn_service(index)
170            .enclosed(Option::<DummyMiddleware>::None)
171            .call(())
172            .now_or_panic()
173            .unwrap();
174
175        let res = service.call("996").now_or_panic().unwrap();
176        assert_eq!(res, "996");
177    }
178}