Skip to main content

rama_core/service/
svc.rs

1//! [`Service`] and [`BoxService`] traits.
2
3use core::convert::Infallible;
4use core::fmt;
5use core::marker::PhantomData;
6use core::pin::Pin;
7
8use crate::std::{boxed::Box, sync::Arc};
9
10/// A [`Service`] that produces rama services,
11/// to serve given an input, be it transport layer Inputs or application layer http requests,
12/// or something else entirely.
13pub trait Service<Input>: Sized + Send + Sync + 'static {
14    /// The type of the output returned by the service.
15    type Output: Send + 'static;
16
17    /// The type of error returned by the service.
18    type Error: Send + 'static;
19
20    /// Serve an output or an error for the given input
21    fn serve(
22        &self,
23        input: Input,
24    ) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send + '_;
25
26    /// Box this service to allow for dynamic dispatch.
27    fn boxed(self) -> BoxService<Input, Self::Output, Self::Error> {
28        BoxService::new(self)
29    }
30}
31
32impl<Input> Service<Input> for ()
33where
34    Input: Send + 'static,
35{
36    type Output = Input;
37    type Error = Infallible;
38
39    async fn serve(&self, input: Input) -> Result<Self::Output, Self::Error> {
40        Ok(input)
41    }
42}
43
44impl<S, Input> Service<Input> for Arc<S>
45where
46    S: Service<Input>,
47{
48    type Output = S::Output;
49    type Error = S::Error;
50
51    #[inline]
52    fn serve(
53        &self,
54        input: Input,
55    ) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send + '_ {
56        self.as_ref().serve(input)
57    }
58}
59
60impl<S, Input> Service<Input> for &'static S
61where
62    S: Service<Input>,
63{
64    type Output = S::Output;
65    type Error = S::Error;
66
67    #[inline(always)]
68    fn serve(
69        &self,
70        input: Input,
71    ) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send + '_ {
72        (**self).serve(input)
73    }
74}
75
76impl<S, Input> Service<Input> for Box<S>
77where
78    S: Service<Input>,
79{
80    type Output = S::Output;
81    type Error = S::Error;
82
83    #[inline]
84    fn serve(
85        &self,
86
87        input: Input,
88    ) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send + '_ {
89        self.as_ref().serve(input)
90    }
91}
92
93/// Internal trait for dynamic dispatch of Async Traits,
94/// implemented according to the pioneers of this Design Pattern
95/// found at <https://rust-lang.github.io/async-fundamentals-initiative/evaluation/case-studies/builder-provider-api.html#dynamic-dispatch-behind-the-api>
96/// and widely published at <https://blog.rust-lang.org/inside-rust/2023/05/03/stabilizing-async-fn-in-trait.html>.
97trait DynService<Input> {
98    type Output;
99    type Error;
100
101    #[expect(clippy::type_complexity)]
102    fn serve_box(
103        &self,
104        input: Input,
105    ) -> Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send + '_>>;
106}
107
108impl<Input, T> DynService<Input> for T
109where
110    T: Service<Input>,
111{
112    type Output = T::Output;
113    type Error = T::Error;
114
115    fn serve_box(
116        &self,
117        input: Input,
118    ) -> Pin<Box<dyn Future<Output = Result<Self::Output, Self::Error>> + Send + '_>> {
119        Box::pin(self.serve(input))
120    }
121}
122
123/// A boxed [`Service`], to serve Inputs with,
124/// for where you inputuire dynamic dispatch.
125pub struct BoxService<Input, Output, Error> {
126    inner: Arc<dyn DynService<Input, Output = Output, Error = Error> + Send + Sync + 'static>,
127}
128
129impl<Input, Output, Error> Clone for BoxService<Input, Output, Error> {
130    fn clone(&self) -> Self {
131        Self {
132            inner: self.inner.clone(),
133        }
134    }
135}
136
137impl<Input, Output, Error> BoxService<Input, Output, Error> {
138    /// Create a new [`BoxService`] from the given service.
139    #[inline]
140    pub fn new<T>(service: T) -> Self
141    where
142        T: Service<Input, Output = Output, Error = Error>,
143    {
144        Self {
145            inner: Arc::new(service),
146        }
147    }
148}
149
150impl<Input, Output, Error> core::fmt::Debug for BoxService<Input, Output, Error> {
151    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
152        f.debug_struct("BoxService").finish()
153    }
154}
155
156impl<Input, Output, Error> Service<Input> for BoxService<Input, Output, Error>
157where
158    Input: 'static,
159    Output: Send + 'static,
160    Error: Send + 'static,
161{
162    type Output = Output;
163    type Error = Error;
164
165    #[inline]
166    fn serve(
167        &self,
168
169        input: Input,
170    ) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send + '_ {
171        self.inner.serve_box(input)
172    }
173
174    #[inline]
175    fn boxed(self) -> Self {
176        self
177    }
178}
179
180macro_rules! impl_service_either {
181    ($id:ident, $first:ident $(, $param:ident)* $(,)?) => {
182        impl<$first, $($param,)* Input, Output> Service<Input> for crate::combinators::$id<$first $(,$param)*>
183        where
184            $first: Service<Input, Output = Output>,
185            $(
186                $param: Service<Input, Output = Output, Error: Into<$first::Error>>,
187            )*
188            Input: Send + 'static,
189            Output: Send + 'static,
190        {
191            type Output = Output;
192            type Error = $first::Error;
193
194            async fn serve(&self, input: Input) -> Result<Self::Output, Self::Error> {
195                match self {
196                    crate::combinators::$id::$first(s) => s.serve(input).await,
197                    $(
198                        crate::combinators::$id::$param(s) => s.serve(input).await.map_err(Into::into),
199                    )*
200                }
201            }
202        }
203    };
204}
205
206crate::combinators::impl_either!(impl_service_either);
207
208#[non_exhaustive]
209#[derive(Debug, Clone, Copy, Default)]
210/// A [`Service`] which will simply return the given input as Ok(_),
211/// with an [`Infallible`] error.
212pub struct MirrorService;
213
214impl MirrorService {
215    /// Create a new [`MirrorService`].
216    #[inline(always)]
217    #[must_use]
218    pub fn new() -> Self {
219        Self
220    }
221}
222
223impl<Input> Service<Input> for MirrorService
224where
225    Input: Send + 'static,
226{
227    type Output = Input;
228    type Error = Infallible;
229
230    #[inline]
231    fn serve(
232        &self,
233        input: Input,
234    ) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send + '_ {
235        core::future::ready(Ok(input))
236    }
237}
238
239rama_utils::macros::error::static_str_error! {
240    #[doc = "Input rejected"]
241    pub struct RejectError;
242}
243
244/// A [`Service`] which always rejects with an error.
245pub struct RejectService<R = (), E = RejectError> {
246    error: E,
247    _phantom: PhantomData<fn() -> R>,
248}
249
250impl Default for RejectService {
251    fn default() -> Self {
252        Self {
253            error: RejectError,
254            _phantom: PhantomData,
255        }
256    }
257}
258
259impl<R, E: Clone + Send + Sync + 'static> RejectService<R, E> {
260    /// Create a new [`RejectService`].
261    pub fn new(error: E) -> Self {
262        Self {
263            error,
264            _phantom: PhantomData,
265        }
266    }
267}
268
269impl<R, E: Clone> Clone for RejectService<R, E> {
270    fn clone(&self) -> Self {
271        Self {
272            error: self.error.clone(),
273            _phantom: PhantomData,
274        }
275    }
276}
277
278impl<R, E: fmt::Debug> fmt::Debug for RejectService<R, E> {
279    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280        f.debug_struct("RejectService")
281            .field("error", &self.error)
282            .field(
283                "_phantom",
284                &format_args!("{}", core::any::type_name::<fn() -> R>()),
285            )
286            .finish()
287    }
288}
289
290impl<Input, Output, Error> Service<Input> for RejectService<Output, Error>
291where
292    Input: 'static,
293    Output: Send + 'static,
294    Error: Clone + Send + Sync + 'static,
295{
296    type Output = Output;
297    type Error = Error;
298
299    #[inline]
300    fn serve(
301        &self,
302
303        _input: Input,
304    ) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send + '_ {
305        let error = self.error.clone();
306        core::future::ready(Err(error))
307    }
308}
309
310/// A static [`Service`] that always returns pre-defined output.
311#[derive(Debug, Clone)]
312pub struct StaticOutput<O>(O);
313
314impl<O> StaticOutput<O>
315where
316    O: Clone + Send + Sync + 'static,
317{
318    /// Create a new [`StaticOutput`] with the given value.
319    #[inline(always)]
320    pub fn new(value: O) -> Self {
321        Self(value)
322    }
323}
324
325impl<I, O> Service<I> for StaticOutput<O>
326where
327    I: Send + 'static,
328    O: Clone + Send + Sync + 'static,
329{
330    type Output = O;
331    type Error = Infallible;
332
333    async fn serve(&self, _: I) -> Result<Self::Output, Self::Error> {
334        Ok(self.0.clone())
335    }
336}
337
338#[cfg(test)]
339mod tests {
340    use super::*;
341    use core::convert::Infallible;
342
343    #[derive(Debug)]
344    struct AddSvc(usize);
345
346    impl Service<usize> for AddSvc {
347        type Output = usize;
348        type Error = Infallible;
349
350        async fn serve(&self, input: usize) -> Result<Self::Output, Self::Error> {
351            Ok(self.0 + input)
352        }
353    }
354
355    #[derive(Debug)]
356    struct MulSvc(usize);
357
358    impl Service<usize> for MulSvc {
359        type Output = usize;
360        type Error = Infallible;
361
362        async fn serve(&self, input: usize) -> Result<Self::Output, Self::Error> {
363            Ok(self.0 * input)
364        }
365    }
366
367    #[test]
368    fn assert_send() {
369        use rama_utils::test_helpers::*;
370
371        assert_send::<AddSvc>();
372        assert_send::<MulSvc>();
373        assert_send::<BoxService<(), (), ()>>();
374        assert_send::<RejectService>();
375    }
376
377    #[test]
378    fn assert_sync() {
379        use rama_utils::test_helpers::*;
380
381        assert_sync::<AddSvc>();
382        assert_sync::<MulSvc>();
383        assert_sync::<BoxService<(), (), ()>>();
384        assert_sync::<RejectService>();
385    }
386
387    #[tokio::test]
388    async fn add_svc() {
389        let svc = AddSvc(1);
390
391        let output = svc.serve(1).await.unwrap();
392        assert_eq!(output, 2);
393    }
394
395    #[tokio::test]
396    async fn static_dispatch() {
397        let services = vec![AddSvc(1), AddSvc(2), AddSvc(3)];
398
399        for (i, svc) in services.into_iter().enumerate() {
400            let output = svc.serve(i).await.unwrap();
401            assert_eq!(output, i * 2 + 1);
402        }
403    }
404
405    #[tokio::test]
406    async fn dynamic_dispatch() {
407        let services = vec![
408            AddSvc(1).boxed(),
409            AddSvc(2).boxed(),
410            AddSvc(3).boxed(),
411            MulSvc(4).boxed(),
412            MulSvc(5).boxed(),
413        ];
414
415        for (i, svc) in services.into_iter().enumerate() {
416            let output = svc.serve(i).await.unwrap();
417            if i < 3 {
418                assert_eq!(output, i * 2 + 1);
419            } else {
420                assert_eq!(output, i * (i + 1));
421            }
422        }
423    }
424
425    #[tokio::test]
426    async fn service_arc() {
427        let svc = crate::std::sync::Arc::new(AddSvc(1));
428
429        let output = svc.serve(1).await.unwrap();
430        assert_eq!(output, 2);
431    }
432
433    #[tokio::test]
434    async fn box_service_arc() {
435        let svc = crate::std::sync::Arc::new(AddSvc(1)).boxed();
436
437        let output = svc.serve(1).await.unwrap();
438        assert_eq!(output, 2);
439    }
440
441    #[tokio::test]
442    async fn reject_svc() {
443        let svc = RejectService::default();
444
445        let err = svc.serve(1).await.unwrap_err();
446        assert_eq!(err.to_string(), RejectError::new().to_string());
447    }
448}