enfync/pending_result.rs
1//local shortcuts
2use crate::*;
3
4//third-party shortcuts
5
6//standard shortcuts
7use std::fmt::Debug;
8
9//-------------------------------------------------------------------------------------------------------------------
10
11/// Error that can be emitted by a [`PendingResult`].
12#[derive(Debug, Copy, Clone, Eq, PartialEq)]
13pub enum ResultError
14{
15 /// Result has already been taken.
16 Taken,
17 /// The task failed for some reason.
18 TaskFailure,
19}
20
21//-------------------------------------------------------------------------------------------------------------------
22
23/// The pending result of async work.
24#[derive(Debug)]
25pub struct PendingResult<R>
26{
27 result_receiver: Option<Box<dyn ResultReceiver<Result = R> + Send + Sync>>,
28}
29
30impl<R: Debug + Send + Sync + 'static> PendingResult<R>
31{
32 /// Make a new pending result.
33 pub fn new(receiver: impl ResultReceiver<Result = R> + Send + Sync + 'static) -> Self
34 {
35 Self{ result_receiver: Some(Box::new(receiver)) }
36 }
37
38 /// Make a pending result that is immediately ready.
39 pub fn make_ready(result: R) -> Self
40 {
41 Self{ result_receiver: Some(Box::new(ImmedateResultReceiver::new(result))) }
42 }
43
44 /// Check if the result is available.
45 pub fn has_result(&self) -> bool
46 {
47 match &self.result_receiver
48 {
49 // has result if done running
50 Some(receiver) => receiver.done(),
51 // result was already extracted
52 None => false
53 }
54 }
55
56 /// Check if work is done (the result may be unavailable if it was already extracted).
57 /// - This is robust for checking if a result-less task has completed (i.e. `PendingResult<()>`).
58 pub fn done(&self) -> bool
59 {
60 if self.has_result() || self.result_receiver.is_none() { return true; }
61 false
62 }
63
64 /// Extract the result if available (non-blocking).
65 ///
66 /// Returns `None` if the result is still pending.
67 pub fn try_extract(&mut self) -> Option<Result<R, ResultError>>
68 {
69 // check if result is pending
70 if !self.has_result() && self.result_receiver.is_some() { return None; }
71
72 // extract result
73 match &mut self.result_receiver
74 {
75 Some(receiver) => receiver.try_get(),
76 None => Some(Err(ResultError::Taken)),
77 }
78 }
79
80 /// Extract the result (async).
81 ///
82 /// This method is not cancellation-safe.
83 pub async fn extract(&mut self) -> Result<R, ResultError>
84 {
85 // consume the result receiver
86 let Some(receiver) = self.result_receiver.take() else { return Err(ResultError::Taken); };
87
88 // await result
89 receiver.get().await
90 }
91}
92
93//-------------------------------------------------------------------------------------------------------------------
94
95/// Only available on non-WASM targets.
96#[cfg(not(target_family = "wasm"))]
97pub mod blocking
98{
99 /// Extract a pending result while blocking the current thread.
100 ///
101 /// Not available on WASM targets.
102 pub fn extract<R>(mut pending_result: super::PendingResult<R>) -> Result<R, super::ResultError>
103 where
104 R: Send + Sync + std::fmt::Debug + 'static
105 {
106 futures::executor::block_on(async move { pending_result.extract().await })
107 }
108}
109
110//-------------------------------------------------------------------------------------------------------------------