use std::time::Duration; use rand::Rng; use tracing::debug; use tracing::error; const INITIAL_DELAY_MS: u64 = 200; const BACKOFF_FACTOR: f64 = 2.0; pub(crate) fn backoff(attempt: u64) -> Duration { let exp = BACKOFF_FACTOR.powi(attempt.saturating_sub(1) as i32); let base = (INITIAL_DELAY_MS as f64 * exp) as u64; let jitter = rand::rng().random_range(0.9..1.1); Duration::from_millis((base as f64 * jitter) as u64) } pub(crate) fn error_or_panic(message: String) { if cfg!(debug_assertions) || env!("CARGO_PKG_VERSION").contains("alpha") { panic!("{message}"); } else { error!("{message}"); } } pub(crate) fn try_parse_error_message(text: &str) -> String { debug!("Parsing server error response: {}", text); let json = serde_json::from_str::(text).unwrap_or_default(); if let Some(error) = json.get("error") && let Some(message) = error.get("message") && let Some(message_str) = message.as_str() { return message_str.to_string(); } if text.is_empty() { return "Unknown error".to_string(); } text.to_string() } #[cfg(test)] mod tests { use super::*; #[test] fn test_try_parse_error_message() { let text = r#"{ "error": { "message": "Your refresh token has already been used to generate a new access token. Please try signing in again.", "type": "invalid_request_error", "param": null, "code": "refresh_token_reused" } }"#; let message = try_parse_error_message(text); assert_eq!( message, "Your refresh token has already been used to generate a new access token. Please try signing in again." ); } #[test] fn test_try_parse_error_message_no_error() { let text = r#"{"message": "test"}"#; let message = try_parse_error_message(text); assert_eq!(message, r#"{"message": "test"}"#); } }