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}