context_async/
context.rs

1use std::future::Future;
2use std::time;
3#[cfg(feature = "name")]
4use crate::name::Name;
5use crate::{Error, Timer};
6
7/// The [`Context`] trait defines the required methods for `Context`.
8/// It can define a duration, be cancellable, and immediately cancel
9/// async functions if the processing time exceeds the allowed
10/// duration without being cancelled. Additionally, the `Context`
11/// can spawn child contexts.
12///
13/// # Examples
14/// ```
15/// use context_async::{Context, Timer};
16/// async fn my_function() {}
17///
18/// # tokio_test::block_on(async {
19/// let timer = Timer::background(); // a new context without duration limit///
20/// timer.handle(my_function()).await.unwrap();
21/// # });
22/// ```
23///
24/// In addition to using `handle`, you can also handle it
25/// with the `future.with()` method by simply importing the `With` trait.
26///
27/// ```
28/// use context_async::{With, Context, Timer};
29/// async fn my_function() {}
30///
31/// let timer = Timer::todo(); // same as background().
32/// # tokio_test::block_on(async {
33/// my_function()
34///     .with(timer)
35///     .await
36///     .unwrap();
37/// # });
38/// ```
39#[async_trait::async_trait]
40pub trait Context: Clone + Send + Sync {
41    type SubContext: Context;
42
43    /// return the basic [`Timer`].
44    fn timer(&self) -> Timer;
45
46    /// return the name of this context
47    #[cfg(feature = "name")]
48    async fn name(&self) -> Name {
49        self.timer().name().await
50    }
51
52    /// return the deadline [`time::Instant`] of this context.
53    /// return [None] when this context doesn't have deadline.
54    async fn deadline(&self) -> Option<time::Instant> {
55        self.timer().deadline().await
56    }
57
58    /// cancel this context, then cancel all its childs.
59    async fn cancel(&self) {
60        self.timer().cancel().await
61    }
62
63    /// check whether this context is cancelled or not.
64    async fn is_cancelled(&self) -> bool {
65        self.timer().is_cancelled().await
66    }
67
68    /// check whether this context is timeout or not.
69    async fn is_timeout(&self) -> bool {
70        self.timer().is_timeout().await
71    }
72
73    /// check whether there is an [`Error`] in context.
74    async fn error(&self) -> Option<Error> {
75        if self.is_cancelled().await {
76            Some(Error::ContextCancelled)
77        } else if self.is_timeout().await {
78            Some(Error::ContextTimeout)
79        } else {
80            None
81        }
82    }
83
84    /// spawn a new child context.
85    ///
86    /// When the parent (self) is cancelled (call by [`Self::cancel`]),
87    /// the child context will be cancelled too.
88    ///
89    /// # Example
90    /// ```rust
91    ///
92    /// use context_async::{Context, Timer};
93    ///
94    /// tokio_test::block_on(async {
95    /// let ctx = Timer::background();
96    /// let child = ctx.spawn().await;
97    /// let child_of_child = child.spawn().await;
98    ///
99    /// ctx.cancel().await; // <- parent is cancelled.
100    ///
101    /// assert!(child.is_cancelled().await); // the child is cancelled too.
102    /// assert!(child_of_child.is_cancelled().await); // also cancelled.
103    /// });
104    ///
105    /// ```
106    async fn spawn(&self) -> Self::SubContext;
107
108    /// spawn a new child context, with a new timeout parameter.
109    ///
110    /// Same as [`Self::spawn`], when the parent (self) is cancelled (call by [`Self::cancel`]),
111    /// the child context will be cancelled too.
112    ///
113    /// The expire_at instant should not be longer than the parent's expire_at.
114    ///
115    /// # Note
116    /// see [`Self::spawn`] for more examples.
117    async fn spawn_with_timeout(&self, timeout: time::Duration) -> Self::SubContext;
118
119    /// spawn a new child context, with a new timeout parameter in seconds.
120    async fn spawn_in_seconds(&self, secs: u64) -> Self::SubContext {
121        self.spawn_with_timeout(time::Duration::from_secs(secs)).await
122    }
123
124    /// spawn a new child context, with a new timeout parameter in milliseconds.
125    async fn spawn_in_milliseconds(&self, millis: u64) -> Self::SubContext {
126        self.spawn_with_timeout(time::Duration::from_millis(millis)).await
127    }
128
129    /// handle a future
130    ///
131    /// # Examples
132    /// ```rust
133    /// use context_async::{Context, Timer};
134    ///
135    /// # tokio_test::block_on(async {
136    ///  let ctx = Timer::background();
137    ///  let task = tokio::time::sleep(tokio::time::Duration::from_secs(1));
138    ///
139    ///  ctx.handle(task).await.unwrap();
140    /// # });
141    ///
142    /// ```
143    async fn handle<'a, Fut, T>(&self, fut: Fut) -> crate::Result<T>
144    where
145        Fut: Future<Output = T> + Send + 'a
146    {
147        self.timer().handle(fut).await
148    }
149
150    /// handle a future that returns Result<T, E>.
151    ///
152    /// # Note
153    /// `E` is user-defined error, which implements `From<Error>`.
154    ///
155    /// # Examples
156    ///
157    /// ```rust
158    /// use context_async::{Context, Timer};
159    ///
160    /// #[derive(Debug, Clone)]
161    /// struct MyError;
162    ///
163    /// impl From<context_async::Error> for MyError {
164    ///     fn from(value: context_async::Error) -> Self {
165    ///         MyError
166    ///     }
167    /// }
168    ///
169    /// async fn my_func() -> Result<u8, MyError> {
170    ///     Ok(42)
171    /// }
172    ///
173    /// # tokio_test::block_on(async {
174    /// let ctx = Timer::background();
175    /// let task = my_func();
176    /// let _ = ctx.handle_result(task).await;
177    /// # });
178    /// ```
179    async fn handle_result<'a, Fut, T, E>(&self, fut: Fut) -> Result<T, E>
180    where
181        Fut: Future<Output = Result<T, E>> + Send + 'a,
182        E: From<Error>,
183    {
184        self.timer().handle(fut).await?
185    }
186}
187
188#[async_trait::async_trait]
189impl<T: Context> Context for &T {
190    type SubContext = T::SubContext;
191
192    fn timer(&self) -> Timer {
193        (*self).timer()
194    }
195
196    async fn spawn(&self) -> T::SubContext {
197        (*self).spawn().await
198    }
199
200    async fn spawn_with_timeout(&self, timeout: time::Duration) -> T::SubContext {
201        (*self).spawn_with_timeout(timeout).await
202    }
203}