Skip to main content

ntex_service/
lib.rs

1//! See [`Service`] docs for information on this crate's foundational trait.
2#![deny(clippy::pedantic)]
3#![allow(
4    clippy::cast_possible_truncation,
5    clippy::missing_fields_in_debug,
6    clippy::missing_errors_doc,
7    clippy::missing_panics_doc,
8    clippy::must_use_candidate,
9    clippy::type_complexity,
10    clippy::unused_async,
11    clippy::unused_async_trait_impl
12)]
13use std::rc::Rc;
14
15mod and_then;
16mod apply;
17pub mod boxed;
18pub mod cfg;
19mod chain;
20mod ctx;
21mod fn_ready;
22mod fn_service;
23mod fn_shutdown;
24mod macros;
25mod map;
26mod map_config;
27mod map_err;
28mod map_init_err;
29mod map_state;
30mod middleware;
31pub mod state;
32mod then;
33mod util;
34
35pub mod pipeline;
36mod pl_factory;
37mod pl_inner;
38mod pl_state;
39
40pub use crate::apply::{apply_fn, apply_fn_factory};
41pub use crate::chain::{ServiceChain, ServiceChainFactory, factory, factory_no_st, service};
42pub use crate::ctx::Ctx;
43pub use crate::fn_service::{fn_factory, fn_factory_with_config, fn_service, fn_service_st};
44pub use crate::map_config::{map_config, unit_config};
45pub use crate::map_state::map_state;
46pub use crate::middleware::{Identity, Middleware, Stack, apply, fn_layer};
47pub use crate::pipeline::Pipeline;
48pub use crate::state::{RequestState, State};
49
50#[deprecated]
51pub use crate::chain::svc;
52
53#[allow(unused_variables)]
54/// An asynchronous function from a `Request` to a `Response`.
55///
56/// The `Service` trait represents a request/response interaction, receiving
57/// requests and returning replies. Conceptually, a service is like a function
58/// with one argument that returns a result asynchronously:
59///
60/// ```rust,ignore
61/// async fn(Request) -> Result<Response, Error>
62/// ```
63///
64/// The `Service` trait generalizes this form. Requests are defined as a generic
65/// type parameter, while responses and other details are defined as associated
66/// types on the trait implementation. This design allows services to accept
67/// many request types and produce a single response type.
68///
69/// Services can also have internal mutable state that influences computation
70/// using `Cell`, `RefCell`, or `Mutex`. Services intentionally do not take
71/// `&mut self` to reduce overhead in common use cases.
72///
73/// `Service` provides a uniform API; the same abstractions can represent both
74/// clients and servers. Services describe only _transformation_ operations,
75/// which encourages simple API surfaces, easier testing, and straightforward
76/// composition.
77///
78/// Services can only be called within a pipeline. The `Pipeline` enforces
79/// shared readiness for all services in the pipeline. To process requests from
80/// one service to another, all services must be ready; otherwise, processing
81/// is paused until that state is achieved.
82///
83/// ```rust
84/// # use std::convert::Infallible;
85/// #
86/// # use ntex_service::{Service, Ctx};
87///
88/// struct MyService;
89///
90/// impl Service<(), u8> for MyService {
91///     type Res = u64;
92///     type Error = Infallible;
93///
94///     async fn call(&self, req: u8, ctx: Ctx<'_, Self>) -> Result<Self::Res, Self::Error> {
95///         Ok(req as u64)
96///     }
97/// }
98/// ```
99///
100/// Sometimes it is not necessary to implement the Service trait. For example, the above service
101/// could be rewritten as a simple function and passed to [`fn_service`](fn_service()).
102///
103/// ```rust,ignore
104/// async fn my_service(req: u8) -> Result<u64, Infallible>;
105/// ```
106///
107/// Service cannot be called directly, it must be wrapped to an instance of [`Pipeline`] or
108/// by using `ctx` argument of the call method in case of chanined services.
109pub trait Service<St, Req> {
110    /// Responses that the service could provide.
111    type Res;
112
113    /// Errors produced by the service while checking readiness or executing a call.
114    type Error;
115
116    /// Processes a request and asynchronously returns the response.
117    ///
118    /// The `call` method can only be invoked within a pipeline, which ensures
119    /// that all services in the pipeline are ready. Implementations of `call`
120    /// must not call `ready`; the `ctx` argument ensures that the service is
121    /// ready before it is invoked.
122    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<Self::Res, Self::Error>;
123
124    #[inline]
125    /// Returns when the service is ready to process requests.
126    ///
127    /// If the service is at capacity, `ready` will not return immediately. The current
128    /// task is notified when the service becomes ready again. This function should
129    /// be called while executing on a task.
130    ///
131    /// **Note:** Pipeline readiness is maintained across all services in the pipeline.
132    /// The pipeline can process requests only if every service in the pipeline is ready.
133    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), Self::Error> {
134        Ok(())
135    }
136
137    #[inline]
138    /// Shuts down the service.
139    ///
140    /// Returns when the service has been properly shut down.
141    async fn shutdown(&self, cfg: Ctx<'_, Self, St>) {}
142
143    #[inline]
144    /// Maps this service's output to a different type, returning a new service.
145    ///
146    /// This is similar to `Option::map` or `Iterator::map`, changing the
147    /// output type of the underlying service.
148    ///
149    /// This function consumes the original service and returns a wrapped version,
150    /// following the pattern of standard library `map` methods.
151    fn map<F, Res>(self, f: F) -> ServiceChain<dev::Map<F, Self, Res>, St, Req>
152    where
153        Self: Sized,
154        F: Fn(Self::Res) -> Res,
155    {
156        service(dev::Map::new(f, self))
157    }
158
159    #[inline]
160    /// Maps this service's error to a different type, returning a new service.
161    ///
162    /// This is similar to `Result::map_err`, changing the error type of the
163    /// underlying service. It is useful, for example, to ensure multiple
164    /// services have the same error type.
165    ///
166    /// This function consumes the original service and returns a wrapped version.
167    fn map_err<F, E>(self, f: F) -> ServiceChain<dev::MapErr<F, Self, E>, St, Req>
168    where
169        Self: Sized,
170        F: Fn(Self::Error) -> E,
171    {
172        service(dev::MapErr::new(f, self))
173    }
174
175    #[inline]
176    /// Call another service after call to this one has resolved successfully.
177    ///
178    /// This function can be used to chain two services together and ensure that
179    /// the second service isn't called until call to the fist service have
180    /// finished. Result of the call to the first service is used as an
181    /// input parameter for the second service's call.
182    ///
183    /// Note that this function consumes the receiving service and returns a
184    /// wrapped version of it.
185    fn and_then<Next, F>(self, f: F) -> ServiceChain<dev::AndThen<Self, Next>, St, Req>
186    where
187        Self: Sized,
188        Next: Service<St, Self::Res, Error = Self::Error>,
189        F: IntoService<Next, St, Self::Res>,
190    {
191        service(dev::AndThen::new(self, f.into_service()))
192    }
193}
194
195/// A factory for creating `Service`s.
196///
197/// This is useful when new `Service`s must be produced dynamically. For example,
198/// a TCP server listener accepts new connections, constructs a new `Service` for
199/// each connection using the `ServiceFactory` trait, and uses that service to
200/// handle inbound requests.
201///
202/// `Config` represents the configuration type for the service factory.
203///
204/// Simple factories can often use [`fn_factory`] or [`fn_factory_with_config`]
205/// to reduce boilerplate.
206pub trait ServiceFactory<St, Req, Cfg = ()> {
207    /// Responses given by the created services.
208    type Res;
209
210    /// Errors produced by the created services.
211    type Error;
212
213    /// The type of `Service` produced by this factory.
214    type Service: Service<St, Req, Res = Self::Res, Error = Self::Error>;
215
216    /// Possible errors encountered during service construction.
217    type InitError;
218
219    /// Creates a new service asynchronously and returns it.
220    async fn create(&self, cfg: &Cfg) -> Result<Self::Service, Self::InitError>;
221
222    #[inline]
223    /// Asynchronously creates a new service and wraps it in a container.
224    async fn pipeline(
225        &self,
226        cfg: &Cfg,
227    ) -> Result<Pipeline<Req, Self::Res, Self::Error>, Self::InitError>
228    where
229        Self: 'static,
230        St: Default + 'static,
231        Req: 'static,
232        Cfg: 'static,
233    {
234        Ok(Pipeline::new(self.create(cfg).await?))
235    }
236
237    #[inline]
238    /// Returns a new service that maps this service's output to a different type.
239    fn map<F, Res>(self, f: F) -> ServiceChainFactory<dev::MapFactory<F, Self, Res>, St, Req, Cfg>
240    where
241        Self: Sized,
242        F: Fn(Self::Res) -> Res + Clone,
243    {
244        factory(dev::MapFactory::new(f, self))
245    }
246
247    #[inline]
248    /// Transforms this service's error into another error,
249    /// producing a new service.
250    fn map_err<F, E>(
251        self,
252        f: F,
253    ) -> ServiceChainFactory<dev::MapErrFactory<F, Self, E>, St, Req, Cfg>
254    where
255        Self: Sized,
256        F: Fn(Self::Error) -> E + Clone,
257    {
258        factory(dev::MapErrFactory::new(f, self))
259    }
260
261    #[inline]
262    /// Maps this factory's initialization error to a different error,
263    /// returning a new service factory.
264    fn map_init_err<F, E>(
265        self,
266        f: F,
267    ) -> ServiceChainFactory<dev::MapInitErr<F, Self, E>, St, Req, Cfg>
268    where
269        Self: Sized,
270        F: Fn(Self::InitError) -> E + Clone,
271    {
272        factory(dev::MapInitErr::new(f, self))
273    }
274
275    /// Call another service after call to this one has resolved successfully.
276    fn and_then<U, F>(self, f: F) -> ServiceChainFactory<dev::AndThenFactory<Self, U>, St, Req, Cfg>
277    where
278        Self: Sized,
279        U: ServiceFactory<St, Self::Res, Cfg, Error = Self::Error, InitError = Self::InitError>,
280        F: IntoServiceFactory<U, St, Self::Res, Cfg>,
281    {
282        factory(dev::AndThenFactory::new(self, f.into_factory()))
283    }
284
285    /// Creates a boxed service factory.
286    fn boxed(
287        self,
288    ) -> boxed::BoxServiceFactory<St, Req, Self::Res, Self::Error, Cfg, Self::InitError>
289    where
290        St: 'static,
291        Req: 'static,
292        Cfg: 'static,
293        Self: Sized + 'static,
294    {
295        boxed::factory(self)
296    }
297}
298
299impl<S, St, Req> Service<St, Req> for &S
300where
301    S: Service<St, Req>,
302{
303    type Res = S::Res;
304    type Error = S::Error;
305
306    #[inline]
307    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
308        ctx.ready(&**self).await
309    }
310
311    #[inline]
312    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
313        ctx.call_nowait(&**self, req).await
314    }
315
316    #[inline]
317    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
318        ctx.shutdown(&**self).await;
319    }
320}
321
322impl<S, St, Req> Service<St, Req> for Box<S>
323where
324    S: Service<St, Req>,
325{
326    type Res = S::Res;
327    type Error = S::Error;
328
329    #[inline]
330    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
331        ctx.ready(&**self).await
332    }
333
334    #[inline]
335    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
336        ctx.call_nowait(&**self, req).await
337    }
338
339    #[inline]
340    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
341        ctx.shutdown(&**self).await;
342    }
343}
344
345impl<S, St, Req> Service<St, Req> for Rc<S>
346where
347    S: Service<St, Req>,
348{
349    type Res = S::Res;
350    type Error = S::Error;
351
352    #[inline]
353    async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), S::Error> {
354        ctx.ready(&**self).await
355    }
356
357    #[inline]
358    async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<S::Res, S::Error> {
359        ctx.call_nowait(&**self, req).await
360    }
361
362    #[inline]
363    async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
364        ctx.shutdown(&**self).await;
365    }
366}
367
368impl<Sf, St, Req, Cfg> ServiceFactory<St, Req, Cfg> for Rc<Sf>
369where
370    Sf: ServiceFactory<St, Req, Cfg>,
371{
372    type Res = Sf::Res;
373    type Error = Sf::Error;
374    type Service = Sf::Service;
375    type InitError = Sf::InitError;
376
377    async fn create(&self, cfg: &Cfg) -> Result<Self::Service, Self::InitError> {
378        self.as_ref().create(cfg).await
379    }
380}
381
382/// Trait for types that can be converted to a `Service`
383pub trait IntoService<S, St, Req>
384where
385    S: Service<St, Req>,
386{
387    /// Convert to a `Service`
388    fn into_service(self) -> S;
389}
390
391/// Trait for types that can be converted to a `ServiceFactory`
392pub trait IntoServiceFactory<Sf, St, Req, Cfg = ()>
393where
394    Sf: ServiceFactory<St, Req, Cfg>,
395{
396    /// Convert `Self` to a `ServiceFactory`
397    fn into_factory(self) -> Sf;
398}
399
400impl<S, St, Req> IntoService<S, St, Req> for S
401where
402    S: Service<St, Req>,
403{
404    #[inline]
405    fn into_service(self) -> S {
406        self
407    }
408}
409
410impl<Sf, St, Req, Cfg> IntoServiceFactory<Sf, St, Req, Cfg> for Sf
411where
412    Sf: ServiceFactory<St, Req, Cfg>,
413{
414    #[inline]
415    fn into_factory(self) -> Sf {
416        self
417    }
418}
419
420/// Check `Service` type
421#[inline(always)]
422#[allow(clippy::inline_always)]
423pub fn __assert_svc<St, Req, Res, Err>(
424    s: impl Service<St, Req, Res = Res, Error = Err>,
425) -> impl Service<St, Req, Res = Res, Error = Err> {
426    s
427}
428
429/// Check `ServiceFactory` type
430#[inline(always)]
431#[allow(clippy::inline_always)]
432pub fn __assert_factory<Sf, St, Req, Res, Err, InitCfg, InitErr>(f: Sf) -> Sf
433where
434    Sf: ServiceFactory<St, Req, InitCfg, Res = Res, Error = Err, InitError = InitErr>,
435{
436    f
437}
438
439pub mod dev {
440    pub use crate::and_then::{AndThen, AndThenFactory};
441    pub use crate::apply::{Apply, ApplyCtx, ApplyFactory};
442    pub use crate::chain::{ServiceChain, ServiceChainFactory};
443    pub use crate::fn_ready::FnReadiness;
444    pub use crate::fn_service::{
445        FnService, FnServiceConfig, FnServiceFactory, FnServiceNoConfig, FnServiceSt,
446        FnServiceStFactory,
447    };
448    pub use crate::fn_shutdown::FnShutdown;
449    pub use crate::map::{Map, MapFactory};
450    pub use crate::map_config::{MapConfig, UnitConfig};
451    pub use crate::map_err::{MapErr, MapErrFactory};
452    pub use crate::map_init_err::MapInitErr;
453    pub use crate::map_state::MapState;
454    pub use crate::middleware::{ApplyMiddleware, FnMiddleware};
455    pub use crate::then::{Then, ThenFactory};
456}