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