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//-------------------------------------------------------------------------------------------------------------------