Skip to main content

neptune_job_queue/
job_completion.rs

1use std::fmt;
2
3use super::errors::JobHandleError;
4use super::traits::JobResult;
5
6/// represents completion state of a job
7#[derive(strum::Display)]
8pub enum JobCompletion {
9    /// The job finished processing normally.
10    Finished(Box<dyn JobResult>),
11
12    /// The job was cancelled before or during processing.
13    Cancelled,
14
15    /// The job panicked during processing.
16    ///
17    /// the payload comes from [tokio::task::JoinError::into_panic()]
18    /// and can be used as input to [std::panic::resume_unwind()]
19    Panicked(Box<dyn std::any::Any + Send + 'static>),
20}
21
22impl fmt::Debug for JobCompletion {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        match self {
25            JobCompletion::Finished(result) => {
26                // Attempt to downcast and debug if the underlying JobResult implements Debug
27                if let Some(debuggable) = result
28                    .as_any()
29                    .downcast_ref::<Box<dyn fmt::Debug + Send + Sync>>()
30                {
31                    write!(f, "Finished({:?})", debuggable)
32                } else {
33                    write!(f, "Finished(Box<dyn JobResult>)")
34                }
35            }
36            JobCompletion::Cancelled => write!(f, "Cancelled"),
37            JobCompletion::Panicked(_) => write!(f, "Panicked(<payload>)"),
38        }
39    }
40}
41
42impl<T: JobResult> From<T> for JobCompletion {
43    fn from(result: T) -> Self {
44        Self::Finished(Box::new(result))
45    }
46}
47
48impl TryFrom<JobCompletion> for Box<dyn JobResult> {
49    type Error = JobHandleError;
50
51    fn try_from(jc: JobCompletion) -> Result<Self, Self::Error> {
52        jc.result()
53    }
54}
55
56impl JobCompletion {
57    /// convert into a JobResult fallibly.
58    pub fn result(self) -> Result<Box<dyn JobResult>, JobHandleError> {
59        match self {
60            JobCompletion::Finished(r) => Ok(r),
61            JobCompletion::Cancelled => Err(JobHandleError::JobCancelled),
62            JobCompletion::Panicked(e) => Err(e.into()),
63        }
64    }
65
66    /// obtain a JobResult reference fallibly
67    pub fn result_ref(&self) -> Option<&dyn JobResult> {
68        match self {
69            JobCompletion::Finished(r) => Some(&**r),
70            _ => None,
71        }
72    }
73
74    /// convert into a JobResult.  panics if job did not finish.
75    pub fn unwrap(self) -> Box<dyn JobResult> {
76        self.result()
77            .unwrap_or_else(|e| panic!("Job did not finish. no result available. {}", e))
78    }
79
80    /// indicates if job finished successfully or not.
81    ///
82    /// if true, [Self::unwrap()] is guaranteed to succeed.
83    /// if false [Self::unwrap_err()] is guaranteed to succeed.
84    pub fn finished(&self) -> bool {
85        match self {
86            JobCompletion::Finished(_) => true,
87            JobCompletion::Cancelled => false,
88            JobCompletion::Panicked(_) => false,
89        }
90    }
91
92    // convert into JobHandleError. panics if job Finished.
93    pub fn unwrap_err(self) -> JobHandleError {
94        self.err()
95            .unwrap_or_else(|| panic!("Job finished. no error available"))
96    }
97
98    // fallibly convert into JobHandleError
99    pub fn err(self) -> Option<JobHandleError> {
100        self.result().err()
101    }
102}