armature_http_client/
error.rs1use std::time::Duration;
4use thiserror::Error;
5
6pub type Result<T> = std::result::Result<T, HttpClientError>;
8
9#[derive(Debug, Error)]
11pub enum HttpClientError {
12 #[error("Request failed after {attempts} attempts: {message}")]
14 RetryExhausted {
15 attempts: u32,
17 message: String,
19 },
20
21 #[error("Circuit breaker is open, request rejected")]
23 CircuitOpen,
24
25 #[error("Request timed out after {0:?}")]
27 Timeout(Duration),
28
29 #[error("Connection error: {0}")]
31 Connection(String),
32
33 #[error("Invalid URL: {0}")]
35 InvalidUrl(String),
36
37 #[error("Failed to build request: {0}")]
39 RequestBuild(String),
40
41 #[error("Response error: {status} - {message}")]
43 Response {
44 status: u16,
46 message: String,
48 },
49
50 #[error("JSON error: {0}")]
52 Json(String),
53
54 #[error("Interceptor error: {0}")]
56 Interceptor(String),
57
58 #[error("HTTP error: {0}")]
60 Http(#[from] reqwest::Error),
61
62 #[error("URL parse error: {0}")]
64 UrlParse(#[from] url::ParseError),
65
66 #[error("I/O error: {0}")]
68 Io(#[from] std::io::Error),
69}
70
71impl HttpClientError {
72 pub fn is_retryable(&self) -> bool {
74 match self {
75 Self::Timeout(_) => true,
76 Self::Connection(_) => true,
77 Self::Http(e) => e.is_timeout() || e.is_connect(),
78 Self::Response { status, .. } => {
79 *status >= 500 || *status == 429
81 }
82 _ => false,
83 }
84 }
85
86 pub fn is_timeout(&self) -> bool {
88 matches!(self, Self::Timeout(_)) || matches!(self, Self::Http(e) if e.is_timeout())
89 }
90
91 pub fn is_connection(&self) -> bool {
93 matches!(self, Self::Connection(_)) || matches!(self, Self::Http(e) if e.is_connect())
94 }
95
96 pub fn status_code(&self) -> Option<u16> {
98 match self {
99 Self::Response { status, .. } => Some(*status),
100 Self::Http(e) => e.status().map(|s| s.as_u16()),
101 _ => None,
102 }
103 }
104}