use backon::ExponentialBuilder;
use reqwest::header::HeaderMap;
use std::time::Duration;
#[must_use]
pub fn default_backoff_builder() -> ExponentialBuilder {
ExponentialBuilder::default()
.with_min_delay(Duration::from_millis(500))
.with_max_delay(Duration::from_secs(8))
.with_max_times(20)
.with_factor(2.0)
.with_jitter()
}
#[must_use]
pub const fn is_retryable_status(code: u16) -> bool {
matches!(code, 408 | 409 | 429 | 500..=599)
}
#[must_use]
pub fn parse_retry_after(headers: &HeaderMap) -> Option<Duration> {
if let Some(v) = headers.get("retry-after-ms")
&& let Ok(s) = v.to_str()
&& let Ok(ms) = s.parse::<u64>()
{
return Some(Duration::from_millis(ms.min(60_000)));
}
if let Some(v) = headers.get("retry-after")
&& let Ok(s) = v.to_str()
&& let Ok(secs) = s.parse::<u64>()
{
return Some(Duration::from_secs(secs.min(60)));
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn retryable_matrix() {
assert!(is_retryable_status(429));
assert!(is_retryable_status(500));
assert!(is_retryable_status(529));
assert!(is_retryable_status(408));
assert!(is_retryable_status(409));
assert!(!is_retryable_status(404));
assert!(!is_retryable_status(400));
}
#[test]
fn retry_after_parse() {
let mut h = HeaderMap::new();
h.insert("retry-after", "120".parse().unwrap());
let d = parse_retry_after(&h).expect("Should parse retry-after");
assert_eq!(d.as_secs(), 60); }
#[test]
fn retry_after_ms() {
let mut h = HeaderMap::new();
h.insert("retry-after-ms", "5000".parse().unwrap());
let d = parse_retry_after(&h).expect("Should parse retry-after-ms");
assert_eq!(d.as_millis(), 5000);
}
}