computation_process/
completable.rs

1use cancel_this::Cancelled;
2use std::fmt::{Display, Formatter};
3
4/// The error type returned by an algorithm when the result is not (yet) available.
5///
6/// The result can be unavailable because the computation was canceled ([`Cancelled`]) or because
7/// the algorithm has not finished computing but reached one of its pre-defined suspend points.
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9#[non_exhaustive]
10pub enum Incomplete {
11    /// The computation has reached a suspend point and can be resumed.
12    Suspended,
13    /// The computation was canceled by an external cancellation token.
14    Cancelled(Cancelled),
15    /// The computation has already completed and cannot produce more results.
16    ///
17    /// This is returned when a [`crate::Computable`] or [`crate::Generatable`] is polled
18    /// after it has already produced its final result.
19    Exhausted,
20}
21
22/// A [`Completable`] result is a value eventually computed by an algorithm where
23/// the computation can be [`Incomplete`] when the value is polled.
24pub type Completable<T> = Result<T, Incomplete>;
25
26impl From<Cancelled> for Incomplete {
27    fn from(value: Cancelled) -> Self {
28        Incomplete::Cancelled(value)
29    }
30}
31
32impl Display for Incomplete {
33    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
34        match self {
35            Incomplete::Suspended => write!(f, "Operation suspended"),
36            Incomplete::Exhausted => write!(f, "Computation exhausted"),
37            Incomplete::Cancelled(c) => write!(f, "{}", c),
38        }
39    }
40}
41
42impl std::error::Error for Incomplete {}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47    use cancel_this::Cancelled;
48
49    #[test]
50    fn test_incomplete_suspended() {
51        let incomplete = Incomplete::Suspended;
52        assert_eq!(incomplete, Incomplete::Suspended);
53        assert_eq!(format!("{}", incomplete), "Operation suspended");
54    }
55
56    #[test]
57    fn test_incomplete_cancelled() {
58        let cancelled = Cancelled::default();
59        let incomplete = Incomplete::Cancelled(cancelled.clone());
60        assert_eq!(incomplete.clone(), Incomplete::Cancelled(cancelled.clone()));
61        // Canceled's Display format may vary, so just check it's not empty
62        let display_str = format!("{}", incomplete);
63        assert!(!display_str.is_empty());
64        // Verify it's not the Suspended message
65        assert_ne!(display_str, "Operation suspended");
66    }
67
68    #[test]
69    fn test_from_cancelled() {
70        let cancelled = Cancelled::default();
71        let incomplete: Incomplete = cancelled.clone().into();
72        assert_eq!(incomplete, Incomplete::Cancelled(cancelled));
73    }
74
75    #[test]
76    fn test_completable_ok() {
77        let result: Completable<i32> = Ok(42);
78        assert_eq!(result, Ok(42));
79    }
80
81    #[test]
82    fn test_completable_err_suspended() {
83        let result: Completable<i32> = Err(Incomplete::Suspended);
84        assert_eq!(result, Err(Incomplete::Suspended));
85    }
86
87    #[test]
88    fn test_completable_err_cancelled() {
89        let cancelled = Cancelled::default();
90        let result: Completable<i32> = Err(Incomplete::Cancelled(cancelled.clone()));
91        assert_eq!(result, Err(Incomplete::Cancelled(cancelled)));
92    }
93
94    #[test]
95    fn test_incomplete_debug() {
96        let incomplete = Incomplete::Suspended;
97        let debug_str = format!("{:?}", incomplete);
98        assert!(debug_str.contains("Suspended"));
99    }
100
101    #[test]
102    fn test_incomplete_clone() {
103        let incomplete1 = Incomplete::Suspended;
104        let incomplete2 = incomplete1.clone();
105        assert_eq!(incomplete1, incomplete2);
106    }
107
108    #[test]
109    fn test_incomplete_hash() {
110        use std::collections::HashSet;
111        let mut set = HashSet::new();
112        set.insert(Incomplete::Suspended);
113        assert!(set.contains(&Incomplete::Suspended));
114    }
115
116    #[test]
117    fn test_incomplete_exhausted() {
118        let incomplete = Incomplete::Exhausted;
119        assert_eq!(incomplete, Incomplete::Exhausted);
120        assert_eq!(format!("{}", incomplete), "Computation exhausted");
121    }
122
123    #[test]
124    fn test_completable_err_exhausted() {
125        let result: Completable<i32> = Err(Incomplete::Exhausted);
126        assert_eq!(result, Err(Incomplete::Exhausted));
127    }
128}