execution_policy/
classify.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum FailureClass {
6 Success,
7 Retryable,
8 Permanent,
9 Ignored,
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum RetryDecision {
15 Retry,
16 Stop,
17}
18
19pub(crate) type ErrorPredicate<E> = Box<dyn Fn(&E) -> bool + Send + Sync>;
20pub(crate) type OutcomeClassifier<T, E> = Box<dyn Fn(&Result<T, E>) -> RetryDecision + Send + Sync>;
21
22pub(crate) enum Classifier<T, E> {
25 RetryAll,
27 WhenErr(ErrorPredicate<E>),
29 WhenOutcome(OutcomeClassifier<T, E>),
31}
32
33impl<T, E> Classifier<T, E> {
34 pub(crate) fn decide(&self, outcome: &Result<T, E>) -> RetryDecision {
35 match self {
36 Self::RetryAll => match outcome {
37 Ok(_) => RetryDecision::Stop,
38 Err(_) => RetryDecision::Retry,
39 },
40 Self::WhenErr(pred) => match outcome {
41 Ok(_) => RetryDecision::Stop,
42 Err(e) => {
43 if pred(e) {
44 RetryDecision::Retry
45 } else {
46 RetryDecision::Stop
47 }
48 }
49 },
50 Self::WhenOutcome(f) => f(outcome),
51 }
52 }
53}
54
55impl<T, E> std::fmt::Debug for Classifier<T, E> {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 let name = match self {
58 Self::RetryAll => "RetryAll",
59 Self::WhenErr(_) => "WhenErr",
60 Self::WhenOutcome(_) => "WhenOutcome",
61 };
62 f.debug_tuple("Classifier").field(&name).finish()
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn retry_all_retries_errors_stops_on_ok() {
72 let c: Classifier<u32, &str> = Classifier::RetryAll;
73 assert_eq!(c.decide(&Ok(1)), RetryDecision::Stop);
74 assert_eq!(c.decide(&Err("x")), RetryDecision::Retry);
75 }
76
77 #[test]
78 fn when_err_uses_predicate() {
79 let c: Classifier<u32, i32> = Classifier::WhenErr(Box::new(|e: &i32| *e == 503));
80 assert_eq!(c.decide(&Err(503)), RetryDecision::Retry);
81 assert_eq!(c.decide(&Err(400)), RetryDecision::Stop);
82 }
83
84 #[test]
85 fn when_outcome_can_retry_on_ok() {
86 let c: Classifier<u32, &str> =
87 Classifier::WhenOutcome(Box::new(|o: &Result<u32, &str>| match o {
88 Ok(503) => RetryDecision::Retry,
89 _ => RetryDecision::Stop,
90 }));
91 assert_eq!(c.decide(&Ok(503)), RetryDecision::Retry);
92 assert_eq!(c.decide(&Ok(200)), RetryDecision::Stop);
93 }
94}