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 type RetryAfterExtractor<E> = Box<dyn Fn(&E) -> Option<std::time::Duration> + Send + Sync>;
25
26pub(crate) enum Classifier<T, E> {
29 RetryAll,
31 WhenErr(ErrorPredicate<E>),
33 WhenOutcome(OutcomeClassifier<T, E>),
35}
36
37impl<T, E> Classifier<T, E> {
38 pub(crate) fn decide(&self, outcome: &Result<T, E>) -> RetryDecision {
39 match self {
40 Self::RetryAll => match outcome {
41 Ok(_) => RetryDecision::Stop,
42 Err(_) => RetryDecision::Retry,
43 },
44 Self::WhenErr(pred) => match outcome {
45 Ok(_) => RetryDecision::Stop,
46 Err(e) => {
47 if pred(e) {
48 RetryDecision::Retry
49 } else {
50 RetryDecision::Stop
51 }
52 }
53 },
54 Self::WhenOutcome(f) => f(outcome),
55 }
56 }
57}
58
59impl<T, E> std::fmt::Debug for Classifier<T, E> {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 let name = match self {
62 Self::RetryAll => "RetryAll",
63 Self::WhenErr(_) => "WhenErr",
64 Self::WhenOutcome(_) => "WhenOutcome",
65 };
66 f.debug_tuple("Classifier").field(&name).finish()
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn retry_all_retries_errors_stops_on_ok() {
76 let c: Classifier<u32, &str> = Classifier::RetryAll;
77 assert_eq!(c.decide(&Ok(1)), RetryDecision::Stop);
78 assert_eq!(c.decide(&Err("x")), RetryDecision::Retry);
79 }
80
81 #[test]
82 fn when_err_uses_predicate() {
83 let c: Classifier<u32, i32> = Classifier::WhenErr(Box::new(|e: &i32| *e == 503));
84 assert_eq!(c.decide(&Err(503)), RetryDecision::Retry);
85 assert_eq!(c.decide(&Err(400)), RetryDecision::Stop);
86 }
87
88 #[test]
89 fn when_outcome_can_retry_on_ok() {
90 let c: Classifier<u32, &str> =
91 Classifier::WhenOutcome(Box::new(|o: &Result<u32, &str>| match o {
92 Ok(503) => RetryDecision::Retry,
93 _ => RetryDecision::Stop,
94 }));
95 assert_eq!(c.decide(&Ok(503)), RetryDecision::Retry);
96 assert_eq!(c.decide(&Ok(200)), RetryDecision::Stop);
97 }
98}