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}