mod retry;
pub use retry::{RetryConfig, classify_retryable_error, parse_retry_after};
#[derive(Debug, thiserror::Error)]
pub enum HttpError {
#[error("HTTP request failed: {0}")]
Request(#[from] reqwest::Error),
#[error("Interrupted by user")]
Interrupted,
#[error("All retries exhausted: {message}")]
RetriesExhausted { message: String },
#[error("Authentication error: {0}")]
Auth(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("{0}")]
Other(String),
}
#[derive(Debug)]
pub struct HttpResult {
pub success: bool,
pub status: Option<u16>,
pub body: Option<serde_json::Value>,
pub error: Option<String>,
pub interrupted: bool,
pub retryable: bool,
pub request_id: Option<String>,
pub retry_after: Option<String>,
pub retry_after_ms: Option<String>,
}
impl HttpResult {
pub fn ok(status: u16, body: serde_json::Value) -> Self {
Self {
success: true,
status: Some(status),
body: Some(body),
error: None,
interrupted: false,
retryable: false,
request_id: None,
retry_after: None,
retry_after_ms: None,
}
}
pub fn fail(error: impl Into<String>, retryable: bool) -> Self {
Self {
success: false,
status: None,
body: None,
error: Some(error.into()),
interrupted: false,
retryable,
request_id: None,
retry_after: None,
retry_after_ms: None,
}
}
pub fn interrupted() -> Self {
Self {
success: false,
status: None,
body: None,
error: Some("Interrupted by user".into()),
interrupted: true,
retryable: false,
request_id: None,
retry_after: None,
retry_after_ms: None,
}
}
pub fn retryable_status(
status: u16,
body: Option<serde_json::Value>,
retry_after: Option<String>,
) -> Self {
Self {
success: false,
status: Some(status),
body,
error: Some(format!("HTTP {status}")),
interrupted: false,
retryable: true,
request_id: None,
retry_after,
retry_after_ms: None,
}
}
pub fn with_request_id(mut self, request_id: impl Into<String>) -> Self {
self.request_id = Some(request_id.into());
self
}
}
#[cfg(test)]
mod tests;