xitca_service/ready/
mod.rs

1//! trait and types for backpressure handling.
2
3mod and_then;
4mod enclosed_fn;
5mod function;
6mod map;
7mod map_err;
8
9use core::{future::Future, ops::Deref, pin::Pin};
10
11/// Extend trait for [Service](crate::Service).
12///
13/// Can be used to cehck the ready state of a service before calling it.
14///
15/// # Examples:
16/// ```rust
17/// # use std::{cell::Cell, rc::Rc, future::Future};
18/// # use xitca_service::{Service, ready::ReadyService};
19///
20/// // a service with conditional availability based on state of Permit.
21/// struct Foo(Permit);
22///
23/// // a permit reset the inner boolean to true on drop.
24/// #[derive(Clone)]
25/// struct Permit(Rc<Cell<bool>>);
26///
27/// impl Drop for Permit {
28///     fn drop(&mut self) {
29///         self.0.set(true);
30///     }
31/// }
32///
33/// impl Service<()> for Foo {
34///     type Response = ();
35///     type Error = ();
36///
37///     async fn call(&self, _req: ()) -> Result<Self::Response, Self::Error> {
38///         Ok(())
39///     }
40/// }
41///
42/// impl ReadyService for Foo {
43///     type Ready = Result<Permit, ()>;
44///
45///     async fn ready(&self) -> Self::Ready {
46///         if self.0.0.get() {
47///             // set permit to false and return with Ok<Permit>
48///             self.0.0.set(false);
49///             Ok(self.0.clone())
50///         } else {
51///             // return error is to simply the example.
52///             // In real world this branch should be an async waiting for Permit reset to true.
53///             Err(())
54///         }                
55///     }
56/// }
57///
58/// async fn workflow(service: &Foo) {
59///     let permit = service.ready().await.unwrap(); // check service ready state.
60///
61///     service.call(()).await.unwrap(); // run Service::call when permit is held in scope.
62///
63///     drop(permit); // drop permit after Service::call is finished.
64/// }
65///
66/// async fn throttle(service: &Foo) {
67///     let permit = service.ready().await.unwrap();
68///     assert!(service.ready().await.is_err());  // service is throttled because permit is still held in scope.
69/// }
70/// ```
71pub trait ReadyService {
72    type Ready;
73
74    fn ready(&self) -> impl Future<Output = Self::Ready>;
75}
76
77#[cfg(feature = "alloc")]
78mod alloc_impl {
79    use super::ReadyService;
80
81    use alloc::{boxed::Box, rc::Rc, sync::Arc};
82
83    macro_rules! impl_alloc {
84        ($alloc: ident) => {
85            impl<S> ReadyService for $alloc<S>
86            where
87                S: ReadyService + ?Sized,
88            {
89                type Ready = S::Ready;
90
91                #[inline]
92                async fn ready(&self) -> Self::Ready {
93                    (**self).ready().await
94                }
95            }
96        };
97    }
98
99    impl_alloc!(Box);
100    impl_alloc!(Rc);
101    impl_alloc!(Arc);
102}
103
104impl<S> ReadyService for Pin<S>
105where
106    S: Deref,
107    S::Target: ReadyService,
108{
109    type Ready = <S::Target as ReadyService>::Ready;
110
111    #[inline]
112    async fn ready(&self) -> Self::Ready {
113        self.as_ref().get_ref().ready().await
114    }
115}