layered/
service.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4/// An async function `In → Out` that processes inputs.
5///
6/// This trait is the foundation for building composable services.
7/// Implement it directly for custom services, or use [`Execute`][crate::Execute]
8/// to wrap closures.
9///
10/// See the [crate documentation][crate] for usage examples and layer composition.
11#[cfg_attr(any(test, feature = "dynamic-service"), dynosaur::dynosaur(pub DynService = dyn(box) Service, bridge(none)))]
12pub trait Service<In>: Send + Sync {
13    /// The output type returned by this service.
14    type Out;
15
16    /// Processes the input and returns the output.
17    ///
18    /// The returned future must be [`Send`] for compatibility with multi-threaded
19    /// async runtimes.
20    ///
21    /// # Examples
22    ///
23    /// ```
24    /// use layered::Service;
25    ///
26    /// struct EchoService;
27    ///
28    /// impl Service<String> for EchoService {
29    ///     type Out = String;
30    ///
31    ///     async fn execute(&self, input: String) -> Self::Out {
32    ///         input
33    ///     }
34    /// }
35    /// ```
36    fn execute(&self, input: In) -> impl Future<Output = Self::Out> + Send;
37}
38
39impl<S, In> Service<In> for Box<S>
40where
41    S: Service<In>,
42{
43    type Out = S::Out;
44
45    fn execute(&self, input: In) -> impl Future<Output = Self::Out> + Send {
46        (**self).execute(input)
47    }
48}
49
50impl<S, In> Service<In> for std::sync::Arc<S>
51where
52    S: Service<In>,
53{
54    type Out = S::Out;
55
56    fn execute(&self, input: In) -> impl Future<Output = Self::Out> + Send {
57        (**self).execute(input)
58    }
59}
60
61#[cfg_attr(coverage_nightly, coverage(off))]
62#[cfg(test)]
63mod tests {
64    use futures::executor::block_on;
65
66    use super::*;
67
68    // A simple service that echoes the input.
69    struct EchoService;
70
71    impl Service<String> for EchoService {
72        type Out = String;
73
74        async fn execute(&self, input: String) -> Self::Out {
75            input
76        }
77    }
78
79    #[test]
80    fn test_echo_service() {
81        let service = EchoService;
82        let output = block_on(service.execute("Hello, World!".to_string()));
83        assert_eq!(output, "Hello, World!");
84    }
85
86    #[test]
87    fn test_boxed_service() {
88        let service: Box<EchoService> = Box::new(EchoService);
89        let output = block_on(service.execute("Hello, Boxed World!".to_string()));
90        assert_eq!(output, "Hello, Boxed World!");
91    }
92
93    #[test]
94    fn test_arc_service() {
95        let service: std::sync::Arc<EchoService> = std::sync::Arc::new(EchoService);
96        let output = block_on(service.execute("Hello, Arc World!".to_string()));
97        assert_eq!(output, "Hello, Arc World!");
98    }
99}