use crate::Outcome;
pub(crate) fn hold_index(retry_count: u32, hold_queue_count: usize) -> usize {
debug_assert!(
hold_queue_count > 0,
"hold_index called with no hold queues"
);
(retry_count as usize).min(hold_queue_count - 1)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum RetryDecision {
Ack,
Dlq { reason: &'static str },
Hold { increment: bool },
}
pub(crate) fn retries_exhausted(retry_count: u32, max_retries: u32) -> bool {
retry_count >= max_retries
}
pub(crate) fn decide_retry(outcome: &Outcome, retry_count: u32, max_retries: u32) -> RetryDecision {
match outcome {
Outcome::Ack => RetryDecision::Ack,
Outcome::Reject => RetryDecision::Dlq { reason: "rejected" },
Outcome::Retry => {
if retries_exhausted(retry_count, max_retries) {
RetryDecision::Dlq {
reason: "max_retries_exceeded",
}
} else {
RetryDecision::Hold { increment: true }
}
}
Outcome::Defer => RetryDecision::Hold { increment: false },
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hold_index_clamps_to_last_tier() {
assert_eq!(hold_index(0, 2), 0);
assert_eq!(hold_index(1, 2), 1);
assert_eq!(hold_index(5, 2), 1); assert_eq!(hold_index(0, 1), 0);
}
#[test]
fn retries_exhausted_at_boundary() {
assert!(!retries_exhausted(2, 3));
assert!(retries_exhausted(3, 3));
assert!(retries_exhausted(0, 0));
}
#[test]
fn ack_decides_ack() {
assert_eq!(decide_retry(&Outcome::Ack, 0, 3), RetryDecision::Ack);
}
#[test]
fn reject_always_dlqs_rejected() {
assert_eq!(
decide_retry(&Outcome::Reject, 0, 3),
RetryDecision::Dlq { reason: "rejected" }
);
assert_eq!(
decide_retry(&Outcome::Reject, 99, 0),
RetryDecision::Dlq { reason: "rejected" }
);
}
#[test]
fn retry_below_budget_holds_and_increments() {
assert_eq!(
decide_retry(&Outcome::Retry, 0, 3),
RetryDecision::Hold { increment: true }
);
}
#[test]
fn retry_at_budget_boundary_dlqs() {
assert_eq!(
decide_retry(&Outcome::Retry, 3, 3),
RetryDecision::Dlq {
reason: "max_retries_exceeded"
}
);
}
#[test]
fn retry_last_allowed_holds() {
assert_eq!(
decide_retry(&Outcome::Retry, 2, 3),
RetryDecision::Hold { increment: true }
);
}
#[test]
fn retry_with_zero_budget_dlqs_immediately() {
assert_eq!(
decide_retry(&Outcome::Retry, 0, 0),
RetryDecision::Dlq {
reason: "max_retries_exceeded"
}
);
}
#[test]
fn defer_always_holds_without_increment() {
assert_eq!(
decide_retry(&Outcome::Defer, 0, 3),
RetryDecision::Hold { increment: false }
);
assert_eq!(
decide_retry(&Outcome::Defer, 99, 0),
RetryDecision::Hold { increment: false }
);
}
}