service_async/
lib.rs

1//! # Service Async
2//! [![Crates.io](https://img.shields.io/crates/v/service-async.svg)](https://crates.io/crates/service-async)
3//!
4//! A `Service` trait similar to tower-service <https://docs.rs/tower/latest/tower/trait.Service.html>, in pure async style
5//!
6//! ## Motivation: Overcoming Limitations in Tower's Service Model
7//!
8//! The Tower framework's `Service` trait, while powerful, presents some challenges:
9//!
10//! 1. Limited Capture Scope: As a future factory used serially and spawned for parallel
11//! execution, Tower's `Service` futures cannot capture `&self` or `&mut self`. This
12//! necessitates cloning and moving ownership into the future.
13//!
14//! 2. Complex Poll-Style Implementation: Tower's `Service` trait is defined in a
15//! poll-style, requiring manual state management. This often leads to verbose
16//! implementations using `Box<Pin<...>>` to leverage async/await syntax.
17//!
18//! These limitations often result in code patterns like:
19//!
20//! ```rust
21//! impl<S, Req> tower::Service<Req> for SomeStruct<S>
22//! where
23//!     // ...
24//! {
25//!     type Response = // ...;
26//!     type Error = // ...;
27//!     type Future = Pin<Box<dyn Future<Output = ...> + Send + 'static>>;
28//!     
29//!     fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
30//!         self.inner.poll_ready(cx)
31//!     }
32//!     
33//!     fn call(&mut self, req: Req) -> Self::Future {
34//!         let client = self.client.clone();
35//!         Box::pin(async move {
36//!             client.get(req).await;
37//!             // ...
38//!         })
39//!     }
40//! }
41//! ```
42//! ## Introducing a Refined Service Trait
43//!
44//! This crate leverages `impl Trait` to introduce a new [`Service`](crate::Service)
45//! trait, designed to simplify implementation and improve performance:
46//!
47//! 1. Efficient Borrowing: By using `impl Trait` in the return position, futures
48//! can now capture `&self` or `&mut self`, eliminating unnecessary cloning.
49//!
50//! 2. Zero-Cost Abstractions: Utilizing `impl Trait` instead of `Box<dyn...>`
51//! allows for more inline code optimization, especially for operations not crossing await points.
52//!
53//! This approach combines the power of `impl Trait` with a refined  [`Service`](crate::Service)
54//! trait to offer both flexibility and performance improvements.
55//!
56//! To enable parallel execution with this new design, we propose two approaches:
57//!
58//! 1. Shared Immutable Access: Use `&self` with a single `Service` instance.
59//! 2. Exclusive Mutable Access: Use `&mut self` and create a new `Service` instance for each call.
60//!
61//! The first approach is generally preferred, as mutable `Service`
62//! instances are often unnecessary for single-use scenarios.
63//!
64//! Our refined [`Service`](crate::Service) trait is defined as:
65//!
66//! ```rust
67//! pub trait Service<Request> {
68//!     /// Responses given by the service.
69//!     type Response;
70//!     /// Errors produced by the service.
71//!     type Error;
72//!     /// Process the request and return the response asynchronously.
73//!     fn call(&self, req: Request) -> impl Future<Output = Result<Self::Response, Self::Error>>;
74//! }
75//! ```
76//!
77//! This design eliminates the need for a `poll_ready` function, as state is maintained within the future itself.
78//!
79//! ## Key Differences and Advantages
80//!
81//! Compared to Tower's approach, our [`Service`](crate::Service) trait represents a paradigm shift:
82//!
83//! - Role: It functions as a request handler rather than a future factory.
84//! - State Management: Mutable state requires explicit synchronization
85//!   primitives like `Mutex` or `RefCell`.
86//! - Resource Efficiency: Our approach maintains reference relationships,
87//!   incurring costs only when mutability is required, unlike Tower's
88//!   shared ownership model where each share has an associated cost.
89//!
90//! This refined [`Service`](crate::Service) trait offers a more intuitive, efficient,
91//! and flexible approach to building asynchronous services in Rust.
92//!
93//! ## MakeService
94//!
95//! The [`MakeService`](crate::MakeService) trait provides a flexible way to construct
96//! service chains while allowing state migration from previous instances. This is
97//! particularly useful when services manage stateful resources like connection pools,
98//! and you need to update the service chain with new configurations while preserving existing resources.
99//!
100//! Key features:
101//!
102//! - `make_via_ref` method allows creating a new service while optionally referencing an existing one.
103//! - Enables state preservation and resource reuse between service instances.
104//! - `make` method provides a convenient way to create a service without an existing reference.
105//!
106//! Example usage:
107//!
108//! ```rust
109//! struct SvcA {
110//!     pass_flag: bool,
111//!     not_pass_flag: bool,
112//! }
113//!
114//! struct InitFlag(bool);
115//!
116//! struct SvcAFactory {
117//!     init_flag: InitFlag,
118//! }
119//!
120//! impl MakeService for SvcAFactory {
121//!     type Service = SvcA;
122//!     type Error = Infallible;
123//!
124//!     fn make_via_ref(&self, old: Option<&Self::Service>) -> Result<Self::Service, Self::Error> {
125//!         Ok(match old {
126//!             // SvcAFactory can access state from the older service
127//!             // which was created.
128//!             Some(r) => SvcA {
129//!                 pass_flag: r.pass_flag,
130//!                 not_pass_flag: self.init_flag.0,
131//!             },
132//!             // There was no older service, so create SvcA from
133//!             // service factory config.
134//!             None => SvcA {
135//!                 pass_flag: self.init_flag.0,
136//!                 not_pass_flag: self.init_flag.0,
137//!             },
138//!         })
139//!     }
140//! }
141//! ```
142//!
143//! This approach allows for efficient updates to service chains, preserving valuable
144//! resources when reconfiguring services.
145//!
146//! # Service Factories and Composition
147//!
148//! ## Service Factories
149//!
150//! In complex systems, creating and managing services often requires more flexibility
151//! than a simple constructor can provide. This is where the concept of Service factories
152//! comes into play. A Service factory is responsible for creating instances of services,
153//! potentially with complex initialization logic or state management.
154//!
155//! ## MakeService Trait
156//!
157//! The [`MakeService`](crate::MakeService) trait is the cornerstone of our Service factory
158//! system. It provides a flexible way to construct service chains while allowing state
159//! migration from previous instances. This is particularly useful when services manage
160//! stateful resources like connection pools, and you need to update the service chain
161//! with new configurations while preserving existing resources.
162//!
163//! Key features of `MakeService`:
164//!
165//! - `make_via_ref` method allows creating a new service while optionally referencing an existing one.
166//! - Enables state preservation and resource reuse between service instances.
167//! - `make` method provides a convenient way to create a service without an existing reference.
168//!
169//! Example usage:
170//!
171//! ```rust
172//! struct SvcA {
173//!     pass_flag: bool,
174//!     not_pass_flag: bool,
175//! }
176//!
177//! struct InitFlag(bool);
178//!
179//! struct SvcAFactory {
180//!     init_flag: InitFlag,
181//! }
182//!
183//! impl MakeService for SvcAFactory {
184//!     type Service = SvcA;
185//!     type Error = Infallible;
186//!
187//!     fn make_via_ref(&self, old: Option<&Self::Service>) -> Result<Self::Service, Self::Error> {
188//!         Ok(match old {
189//!             // SvcAFactory can access state from the older service
190//!             // which was created.
191//!             Some(r) => SvcA {
192//!                 pass_flag: r.pass_flag,
193//!                 not_pass_flag: self.init_flag.0,
194//!             },
195//!             // There was no older service, so create SvcA from
196//!             // service factory config.
197//!             None => SvcA {
198//!                 pass_flag: self.init_flag.0,
199//!                 not_pass_flag: self.init_flag.0,
200//!             },
201//!         })
202//!     }
203//! }
204//! ```
205//!
206//! This approach allows for efficient updates to service chains,
207//! preserving valuable resources when reconfiguring services.
208//!
209//! ## FactoryLayer
210//!
211//! To enable more complex service compositions, we introduce the concept
212//! of [`FactoryLayer`](crate::layer::FactoryLayer). `FactoryLayer` is a
213//! trait that defines how to wrap one factory with another, creating a new
214//! composite factory. This allows for the creation of reusable, modular pieces
215//! of functionality that can be easily combined.
216//!
217//! To simplify chain assembly, factories can define a [`layer`](crate::layer::FactoryLayer::layer)
218//! function that creates a factory wrapper. This concept is similar to the
219//! Tower framework's `Layer`, but with a key difference:
220//!
221//! 1. Tower's `Layer`: Creates a `Service` wrapping an inner `Service`.
222//! 2. Our `layer`: Creates a `Factory` wrapping an inner `Factory`, which can then be used to create the entire `Service` chain.
223//!
224//! ## FactoryStack
225//!
226//! [`FactoryStack`](crate::stack::FactoryStack) is a powerful abstraction that allows
227//! for the creation of complex service chains. It manages a stack of service factories,
228//! providing methods to push new layers onto the stack and to create services from the assembled stack.
229//!
230//! The `FactoryStack` works by composing multiple `FactoryLayer`s together.
231//! Each layer in the stack wraps the layers below it, creating a nested structure
232//! of factories. When you call `make` or `make_async` on a `FactoryStack`, it
233//! traverses this structure from the outermost layer to the innermost, creating the complete service chain.
234//!
235//! This approach allows users to create complex service factories by chaining
236//! multiple factory layers together in an intuitive manner. Each layer can add
237//! its own functionality, modify the behavior of inner layers, or even completely transform the service chain.
238//!
239//! To create a chain of services using `FactoryStack`:
240//!
241//! 1. Start with a `FactoryStack` initialized with your configuration.
242//! 2. Use the `push` method to add layers to the stack.
243//! 3. Each layer can modify or enhance the behavior of the inner layers.
244//! 4. Finally, call `make` or `make_async` to create the complete service chain.
245//!
246//! This system offers a powerful and flexible way to construct and update
247//! service chains while managing state and resources efficiently. It allows for modular,
248//! reusable pieces of functionality, easy reconfiguration of service chains, and clear
249//! separation of concerns between different parts of your service logic.
250//!
251//! ## Putting it all together
252//!
253//! This example demonstrates the practical application of the [`MakeService`](crate::MakeService),
254//! [`FactoryLayer`](crate::layer::FactoryLayer), and [`FactoryStack`](crate::stack::FactoryStack)
255//! concepts. It defines several services (`SvcA` and `SvcB`) and their corresponding factories.
256//! The `FactoryStack` is then used to compose these services in a layered manner. The `Config`
257//! struct provides initial configuration, which is passed through the layers. Finally, in the
258//! `main` function, a service stack is created, combining `SvcAFactory` and `SvcBFactory`.
259//! The resulting service is then called multiple times, showcasing how the chain of services handles requests and maintains state.
260//!
261//! For a more comprehensive example that further illustrates these concepts and their advanced usage,
262//! readers are encouraged to examine the `demo.rs` file in the examples directory of the project.
263//!
264//! ```rust
265//! use std::{
266//!     convert::Infallible,
267//!     sync::atomic::{AtomicUsize, Ordering},
268//! };
269//!
270//! use service_async::{
271//!     layer::{layer_fn, FactoryLayer},
272//!     stack::FactoryStack,
273//!     AsyncMakeService, BoxedMakeService, BoxedService, MakeService, Param, Service,
274//! };
275//!
276//! #[cfg(unix)]
277//! use monoio::main as main_macro;
278//! #[cfg(not(unix))]
279//! use tokio::main as main_macro;
280//!
281//! // ===== Svc*(impl Service) and Svc*Factory(impl NewService) =====
282//!
283//! struct SvcA {
284//!     pass_flag: bool,
285//!     not_pass_flag: bool,
286//! }
287//!
288//! // Implement Service trait for SvcA
289//! impl Service<()> for SvcA {
290//!     type Response = ();
291//!     type Error = Infallible;
292//!
293//!     async fn call(&self, _req: ()) -> Result<Self::Response, Self::Error> {
294//!         println!(
295//!             "SvcA called! pass_flag = {}, not_pass_flag = {}",
296//!             self.pass_flag, self.not_pass_flag
297//!         );
298//!         Ok(())
299//!     }
300//! }
301//!
302//! struct SvcAFactory {
303//!     init_flag: InitFlag,
304//! }
305//!
306//! struct InitFlag(bool);
307//!
308//! impl MakeService for SvcAFactory {
309//!     type Service = SvcA;
310//!     type Error = Infallible;
311//!
312//!     fn make_via_ref(&self, old: Option<&Self::Service>) -> Result<Self::Service, Self::Error> {
313//!         Ok(match old {
314//!             // SvcAFactory can access state from the older service
315//!             // which was created.
316//!             Some(r) => SvcA {
317//!                 pass_flag: r.pass_flag,
318//!                 not_pass_flag: self.init_flag.0,
319//!             },
320//!             // There was no older service, so create SvcA from
321//!             // service factory config.
322//!             None => SvcA {
323//!                 pass_flag: self.init_flag.0,
324//!                 not_pass_flag: self.init_flag.0,
325//!             },
326//!         })
327//!     }
328//! }
329//!
330//! struct SvcB<T> {
331//!     counter: AtomicUsize,
332//!     inner: T,
333//! }
334//!
335//! impl<T> Service<usize> for SvcB<T>
336//! where
337//!     T: Service<(), Error = Infallible>,
338//! {
339//!     type Response = ();
340//!     type Error = Infallible;
341//!
342//!     async fn call(&self, req: usize) -> Result<Self::Response, Self::Error> {
343//!         let old = self.counter.fetch_add(req, Ordering::AcqRel);
344//!         let new = old + req;
345//!         println!("SvcB called! {old}->{new}");
346//!         self.inner.call(()).await?;
347//!         Ok(())
348//!     }
349//! }
350//!
351//! struct SvcBFactory<T>(T);
352//!
353//! impl<T> MakeService for SvcBFactory<T>
354//! where
355//!     T: MakeService<Error = Infallible>,
356//! {
357//!     type Service = SvcB<T::Service>;
358//!     type Error = Infallible;
359//!
360//!     fn make_via_ref(&self, old: Option<&Self::Service>) -> Result<Self::Service, Self::Error> {
361//!         Ok(match old {
362//!             Some(r) => SvcB {
363//!                 counter: r.counter.load(Ordering::Acquire).into(),
364//!                 inner: self.0.make_via_ref(Some(&r.inner))?,
365//!             },
366//!             None => SvcB {
367//!                 counter: 0.into(),
368//!                 inner: self.0.make()?,
369//!             },
370//!         })
371//!     }
372//! }
373//!
374//! // ===== impl layer fn for Factory instead of defining manually =====
375//!
376//! impl SvcAFactory {
377//!     fn layer<C>() -> impl FactoryLayer<C, (), Factory = Self>
378//!     where
379//!         C: Param<InitFlag>,
380//!     {
381//!         layer_fn(|c: &C, ()| SvcAFactory {
382//!             init_flag: c.param(),
383//!         })
384//!     }
385//! }
386//!
387//! impl<T> SvcBFactory<T> {
388//!     fn layer<C>() -> impl FactoryLayer<C, T, Factory = Self> {
389//!         layer_fn(|_: &C, inner| SvcBFactory(inner))
390//!     }
391//! }
392//!
393//!
394//! // ===== Define Config and impl Param<T> for it =====
395//! #[derive(Clone, Copy)]
396//! struct Config {
397//!     init_flag: bool,
398//! }
399//!
400//! impl Param<InitFlag> for Config {
401//!     fn param(&self) -> InitFlag {
402//!         InitFlag(self.init_flag)
403//!     }
404//! }
405//!
406//! #[main_macro]
407//! async fn main() {
408//!     let config = Config { init_flag: false };
409//!     let stack = FactoryStack::new(config)
410//!         .push(SvcAFactory::layer())
411//!         .push(SvcBFactory::layer());
412//!
413//!     let svc = stack.make_async().await.unwrap();
414//!     svc.call(1).await.unwrap();
415//!     svc.call(2).await.unwrap();
416//!     svc.call(3).await.unwrap();
417//! }
418//! ```
419
420use std::future::Future;
421
422/// Provides the `Either` type for flexible service composition and conditional logic in layered architectures.
423pub mod either;
424/// Defines the `FactoryLayer` trait and utilities for creating composable factory wrappers in service architectures.
425pub mod layer;
426/// Provides the `FactoryStack` for composing and managing complex, layered service architectures.
427pub mod stack;
428/// Utilities to work with Serivices &  factories
429pub mod utils;
430
431mod map;
432pub use map::MapTargetService;
433mod boxed;
434
435/// Trait for converting a service into a boxed service.
436pub use boxed::BoxService;
437
438// A factory for creating boxed services.
439pub use boxed::BoxServiceFactory;
440
441/// A type-erased wrapper for asynchronous service factories.
442pub use boxed::BoxedAsyncMakeService;
443
444/// A type-erased wrapper for services, enabling dynamic dispatch.
445pub use boxed::BoxedService;
446
447mod make_service;
448pub use make_service::{
449    ArcMakeBoxedService, ArcMakeService, AsyncMakeService, AsyncMakeServiceWrapper,
450    BoxedMakeBoxedService, BoxedMakeService, MakeService,
451};
452
453/// Item of type T has been set in a certain_map slot.
454pub use param::Param;
455
456/// Item of type T has been set in a certain_map slot and returns a reference.
457pub use param::ParamRef;
458
459/// Item of type T may have been set in a certain_map slot and returns Option<&T>
460pub use param::ParamMaybeRef;
461
462/// Item of type T has been set in a certain_map slot and returns a mutable reference.
463pub use param::ParamMut;
464
465/// Item of type T may have been set in a certain_map slot and returns Option<&mut T>.
466pub use param::ParamMaybeMut;
467
468/// Item of type T is vacant in certain_map slot.
469pub use param::ParamSet;
470
471/// Item of type T can be removed certain_map slot irrespective of it
472/// having been set before.
473pub use param::ParamRemove;
474
475/// Item of type T has been set in certain_map slot and can be removed
476/// from the slot, leaving it vacant.
477pub use param::ParamTake;
478
479/// This `Service` trait leverages `impl Trait` to offer a efficient and flexible
480/// approach to building asynchronous services in Rust. It addresses key challenges
481/// faced with Tower's `Service` trait:
482///
483/// 1. Efficient Borrowing: Futures can capture `&self` or `&mut self`, eliminating
484///    unnecessary cloning.
485/// 2. Simplified Implementation: Removes the need for manual state management and
486///    `Box<Pin<...>>`, allowing for more idiomatic async Rust code.
487///
488/// # Key Features
489///
490/// - Functions as a request handler rather than a future factory.
491/// - Eliminates the need for a `poll_ready` function.
492/// - Allows for more inline code optimization using `impl Trait`.
493/// - Supports both shared immutable (`&self`) and exclusive mutable (`&mut self`) access.
494///
495/// # Resource Efficiency
496///
497/// This design maintains reference relationships, incurring costs only when mutability
498/// is required, unlike Tower's shared ownership model where each share has an associated cost.
499pub trait Service<Request> {
500    /// The type of response returned by this service.
501    type Response;
502
503    /// The type of error that this service can produce.
504    type Error;
505
506    /// Asynchronously process the request and return the response.
507    ///
508    /// This method takes a shared reference to `self`, allowing for efficient
509    /// use of a single `Service` instance across multiple calls. For mutable state,
510    /// consider using synchronization primitives like `Mutex` or `RefCell`.
511    ///
512    /// # Arguments
513    ///
514    /// * `req` - The request to be processed by the service.
515    ///
516    /// # Returns
517    ///
518    /// A `Future` that resolves to a `Result` containing either the service's
519    /// response or an error.
520    fn call(&self, req: Request) -> impl Future<Output = Result<Self::Response, Self::Error>>;
521}