lazy_async_promise/immediatevalue.rs
1use std::future::Future;
2use std::mem;
3use std::sync::Arc;
4
5use tokio::sync::Mutex;
6
7use crate::{BoxedSendError, DirectCacheAccess, FutureResult};
8
9/// # A promise which can be easily created and stored.
10/// ## Introduction
11/// Will spawn a task to resolve the future immediately. No possibility to read out intermediate values or communicate progress.
12/// One can use `Option<ImmediateValuePromise<T>>` inside state structs to make this class somewhat lazy.
13/// That may be an option if you don't need any progress indication or intermediate values.
14/// After the calculation is done, `ImmediateValuePromise<T>` can be read out without mutability requirement using
15/// [`ImmediateValuePromise::get_state`] which also yields an [`ImmediateValueState`] but without requiring `mut`.
16/// Another useful feature after calculation is finished,
17/// is that you can use [`ImmediateValuePromise::poll_state_mut`] to get a mutable [`ImmediateValueState`]
18/// which allows you to take ownership of inner values with [`ImmediateValueState::take_value`] or get a mutable reference
19/// to the inner via [`ImmediateValueState::get_value_mut`].
20/// ## Examples
21/// ### Basic usage
22/// ```rust, no_run
23/// use std::fs::File;
24/// use std::thread;
25/// use std::time::Duration;
26/// use lazy_async_promise::{ImmediateValuePromise, ImmediateValueState};
27/// let mut oneshot_val = ImmediateValuePromise::new(async {
28/// tokio::time::sleep(Duration::from_millis(50)).await;
29/// let test_error_handling = false;
30/// if test_error_handling {
31/// // We can use the ?-operator for most errors in our futures
32/// let _file = File::open("I_DONT_EXIST_ERROR")?;
33/// }
34/// // return the value wrapped in Ok for the result here
35/// Ok(34)
36/// });
37/// assert!(matches!(
38/// oneshot_val.poll_state(),
39/// ImmediateValueState::Updating
40/// ));
41/// thread::sleep(Duration::from_millis(100));
42/// let result = oneshot_val.poll_state();
43/// if let ImmediateValueState::Success(val) = result {
44/// assert_eq!(*val, 34);
45/// } else {
46/// unreachable!();
47/// }
48/// ```
49/// ### Modifying inner values or taking ownership
50/// ```rust, no_run
51/// use std::thread;
52/// use std::time::Duration;
53/// use lazy_async_promise::{DirectCacheAccess, ImmediateValuePromise, ImmediateValueState};
54/// let mut oneshot_val = ImmediateValuePromise::new(async {
55/// Ok(34)
56/// });
57/// thread::sleep(Duration::from_millis(50));
58/// assert!(matches!(
59/// oneshot_val.poll_state(),
60/// ImmediateValueState::Success(_)
61/// ));
62/// let result = oneshot_val.poll_state_mut();
63/// // we got the value, take a mutable ref
64/// if let ImmediateValueState::Success(inner) = result {
65/// *inner=32;
66/// }
67/// else {
68/// unreachable!();
69/// }
70/// assert!(result.get_value_mut().is_some());
71/// // take it out
72/// let value = result.take_value();
73/// assert_eq!(value.unwrap(), 32);
74/// ```
75/// ### Optional laziness
76/// `Option<ImmediateValuePromise>` is a nice way to implement laziness with `get_or_insert`
77/// or `get_or_insert_with`. Unfortunately, using these constructs becomes cumbersome.
78/// To ease the pain, we blanket-implement [`DirectCacheAccess`] for any [`Option<DirectCacheAccess<T>>`].
79///
80/// ```rust, no_run
81/// use std::thread;
82/// use std::time::Duration;
83/// use lazy_async_promise::{DirectCacheAccess, ImmediateValuePromise};
84/// #[derive(Default)]
85/// struct State {
86/// promise: Option<ImmediateValuePromise<i32>>
87/// }
88///
89/// let mut state = State::default();
90/// let promise_ref = state.promise.get_or_insert_with(|| ImmediateValuePromise::new(async {
91/// Ok(34)
92/// }));
93/// promise_ref.poll_state();
94/// thread::sleep(Duration::from_millis(50));
95/// //now let's assume we forgot about our lease already and want to get the value again:
96/// let value_opt = state.promise.as_ref().unwrap().get_value(); // <- dangerous
97/// let value_opt = state.promise.as_ref().and_then(|i| i.get_value()); // <- better, but still ugly
98/// let value_opt = state.promise.get_value(); // <- way nicer!
99/// assert!(value_opt.is_some());
100/// assert_eq!(*value_opt.unwrap(), 34);
101/// ```
102///
103pub struct ImmediateValuePromise<T: Send> {
104 value_arc: Arc<Mutex<Option<FutureResult<T>>>>,
105 state: ImmediateValueState<T>,
106}
107
108/// The return state of a [`ImmediateValuePromise`], contains the error, the value or that it is still updating
109pub enum ImmediateValueState<T> {
110 /// future is not yet resolved
111 Updating,
112 /// future resolved successfully
113 Success(T),
114 /// resolving the future failed somehow
115 Error(BoxedSendError),
116 /// value has been taken out
117 Empty,
118}
119
120impl<T> DirectCacheAccess<T, BoxedSendError> for ImmediateValueState<T> {
121 /// gets a mutable reference to the local cache if existing
122 fn get_value_mut(&mut self) -> Option<&mut T> {
123 match self {
124 ImmediateValueState::Success(payload) => Some(payload),
125 _ => None,
126 }
127 }
128 /// Get the value if possible, [`None`] otherwise
129 fn get_value(&self) -> Option<&T> {
130 if let ImmediateValueState::Success(inner) = self {
131 Some(inner)
132 } else {
133 None
134 }
135 }
136
137 fn get_result(&self) -> Option<Result<&T, &BoxedSendError>> {
138 if let ImmediateValueState::Success(inner) = self {
139 Some(Ok(inner))
140 } else if let ImmediateValueState::Error(error) = self {
141 Some(Err(error))
142 } else {
143 None
144 }
145 }
146
147 /// Takes ownership of the inner value if ready, leaving self in state [`ImmediateValueState::Empty`].
148 /// Does nothing if we are in any other state.
149 fn take_value(&mut self) -> Option<T> {
150 if matches!(self, ImmediateValueState::Success(_)) {
151 let val = mem::replace(self, ImmediateValueState::Empty);
152 return match val {
153 ImmediateValueState::Success(inner) => Some(inner),
154 _ => None,
155 };
156 }
157 None
158 }
159
160 fn take_result(&mut self) -> Option<Result<T, BoxedSendError>> {
161 if matches!(self, ImmediateValueState::Success(_)) {
162 let val = mem::replace(self, ImmediateValueState::Empty);
163 return match val {
164 ImmediateValueState::Success(inner) => Some(Ok(inner)),
165 ImmediateValueState::Error(err) => Some(Err(err)),
166 _ => None,
167 };
168 }
169 None
170 }
171}
172
173impl<T: Send + 'static> DirectCacheAccess<T, BoxedSendError> for ImmediateValuePromise<T> {
174 fn get_value_mut(&mut self) -> Option<&mut T> {
175 self.state.get_value_mut()
176 }
177 fn get_value(&self) -> Option<&T> {
178 self.state.get_value()
179 }
180 fn get_result(&self) -> Option<Result<&T, &BoxedSendError>> {
181 self.state.get_result()
182 }
183 fn take_value(&mut self) -> Option<T> {
184 self.state.take_value()
185 }
186 fn take_result(&mut self) -> Option<Result<T, BoxedSendError>> {
187 self.state.take_result()
188 }
189}
190
191impl<T: Send + 'static> ImmediateValuePromise<T> {
192 /// Creator, supply a future which returns `Result<T, Box<dyn Error + Send>`. Will be immediately spawned via tokio.
193 pub fn new<U: Future<Output = Result<T, BoxedSendError>> + Send + 'static>(updater: U) -> Self {
194 let arc = Arc::new(Mutex::new(None));
195 let arc_clone = arc.clone();
196 tokio::spawn(async move {
197 let mut val = arc_clone.lock().await;
198 *val = Some(updater.await);
199 });
200 Self {
201 value_arc: arc,
202 state: ImmediateValueState::Updating,
203 }
204 }
205
206 /// Poll the state updating the internal state from the running thread if possible, will return the data or error if ready or updating otherwise.
207 pub fn poll_state(&mut self) -> &ImmediateValueState<T> {
208 if matches!(self.state, ImmediateValueState::Updating) {
209 let value = self.value_arc.try_lock();
210 if let Ok(mut guard) = value {
211 if let Some(result) = guard.take() {
212 match result {
213 Ok(value) => self.state = ImmediateValueState::Success(value),
214 Err(e) => self.state = ImmediateValueState::Error(e),
215 };
216 }
217 }
218 }
219 &self.state
220 }
221
222 /// Poll the state, return a mutable ref to to the state
223 pub fn poll_state_mut(&mut self) -> &mut ImmediateValueState<T> {
224 let _ = self.poll_state();
225 &mut self.state
226 }
227
228 /// Get the current state without pulling. No mutability required
229 pub fn get_state(&self) -> &ImmediateValueState<T> {
230 &self.state
231 }
232}
233
234impl<T, V> From<T> for ImmediateValuePromise<V>
235where
236 T: Future<Output = V> + Send + 'static,
237 V: Send + 'static
238{
239 fn from(value: T) -> Self {
240 ImmediateValuePromise::new(async move {
241 Ok(value.await)
242 })
243 }
244}
245
246#[cfg(test)]
247mod test {
248 use std::fs::File;
249 use std::time::Duration;
250
251 use crate::immediatevalue::{ImmediateValuePromise, ImmediateValueState};
252 use crate::DirectCacheAccess;
253
254 #[tokio::test]
255 async fn default() {
256 let mut oneshot_val = ImmediateValuePromise::new(async {
257 tokio::time::sleep(Duration::from_millis(50)).await;
258 Ok(34)
259 });
260 assert!(matches!(
261 oneshot_val.poll_state(),
262 ImmediateValueState::Updating
263 ));
264 tokio::time::sleep(Duration::from_millis(100)).await;
265 let result = oneshot_val.poll_state();
266 if let ImmediateValueState::Success(val) = result {
267 assert_eq!(*val, 34);
268 return;
269 }
270 unreachable!();
271 }
272
273 #[tokio::test]
274 async fn error() {
275 let mut oneshot_val = ImmediateValuePromise::new(async {
276 let some_result = File::open("DOES_NOT_EXIST");
277 some_result?;
278 Ok("bla".to_string())
279 });
280 assert!(matches!(
281 oneshot_val.poll_state(),
282 ImmediateValueState::Updating
283 ));
284 tokio::time::sleep(Duration::from_millis(50)).await;
285 let result = oneshot_val.poll_state();
286 if let ImmediateValueState::Error(e) = result {
287 let _ = format!("{}", **e);
288 return;
289 }
290 unreachable!();
291 }
292
293 #[tokio::test]
294 async fn get_state() {
295 let mut oneshot_val = ImmediateValuePromise::new(async { Ok("bla".to_string()) });
296 // get value does not trigger any polling
297 let state = oneshot_val.get_state();
298 assert!(matches!(state, ImmediateValueState::Updating));
299 tokio::time::sleep(Duration::from_millis(50)).await;
300 let state = oneshot_val.get_state();
301 assert!(matches!(state, ImmediateValueState::Updating));
302
303 let polled = oneshot_val.poll_state();
304 assert_eq!(polled.get_value().unwrap(), "bla");
305 }
306
307 #[tokio::test]
308 async fn get_mut_take_value() {
309 let mut oneshot_val = ImmediateValuePromise::new(async { Ok("bla".to_string()) });
310 tokio::time::sleep(Duration::from_millis(50)).await;
311 {
312 // get value does not trigger any polling
313 let result = oneshot_val.poll_state_mut();
314 // we got the value
315 if let Some(inner) = result.get_value_mut() {
316 assert_eq!(inner, "bla");
317 // write back
318 *inner = "changed".to_string();
319 } else {
320 unreachable!();
321 }
322 let result = oneshot_val.poll_state_mut();
323 // take it out, should be changed and owned
324 let value = result.take_value();
325 assert_eq!(value.unwrap().as_str(), "changed");
326 assert!(matches!(result, ImmediateValueState::Empty));
327 }
328 // afterwards we are empty on get and poll
329 assert!(matches!(
330 oneshot_val.get_state(),
331 ImmediateValueState::Empty
332 ));
333 assert!(matches!(
334 oneshot_val.poll_state(),
335 ImmediateValueState::Empty
336 ));
337 }
338
339 #[tokio::test]
340 async fn option_laziness() {
341 use crate::*;
342 let mut option = Some(ImmediateValuePromise::new(async { Ok("bla".to_string()) }));
343 tokio::time::sleep(Duration::from_millis(50)).await;
344 option.as_mut().unwrap().poll_state();
345 let _inner = option.get_value();
346 let _inner_mut = option.get_value_mut();
347 let inner_owned = option.take_value().unwrap();
348 assert_eq!(inner_owned, "bla");
349 // after value is taken, we can't borrow it again
350 assert!(option.get_value().is_none());
351 assert!(option.get_value_mut().is_none());
352 assert!(option.take_value().is_none());
353 }
354}