nautilus_lighter/http/
error.rs1use nautilus_network::http::HttpClientError;
19use thiserror::Error;
20
21pub type LighterHttpResult<T> = Result<T, LighterHttpError>;
23
24#[derive(Debug, Clone, Error)]
26pub enum LighterHttpError {
27 #[error("network error: {0}")]
29 Network(String),
30 #[error("HTTP {status}: {body}")]
32 Http { status: u16, body: String },
33 #[error("rate limit exceeded: {0}")]
35 RateLimit(String),
36 #[error("venue error {code}: {message}")]
38 Venue { code: i64, message: String },
39 #[error("{data_type} history incomplete after {pages} pages")]
41 HistoryIncomplete {
42 data_type: &'static str,
43 pages: usize,
44 },
45 #[error("parse error: {0}")]
47 Parse(String),
48}
49
50impl From<HttpClientError> for LighterHttpError {
51 fn from(error: HttpClientError) -> Self {
52 Self::Network(error.to_string())
53 }
54}
55
56impl From<serde_json::Error> for LighterHttpError {
57 fn from(error: serde_json::Error) -> Self {
58 Self::Parse(error.to_string())
59 }
60}
61
62impl From<anyhow::Error> for LighterHttpError {
63 fn from(error: anyhow::Error) -> Self {
64 Self::Parse(error.to_string())
65 }
66}
67
68#[must_use]
73pub fn should_retry_lighter_http_error(error: &LighterHttpError) -> bool {
74 match error {
75 LighterHttpError::Network(_) | LighterHttpError::RateLimit(_) => true,
76 LighterHttpError::Http { status, .. } => *status >= 500,
77 LighterHttpError::Venue { .. }
78 | LighterHttpError::HistoryIncomplete { .. }
79 | LighterHttpError::Parse(_) => false,
80 }
81}
82
83#[must_use]
85pub fn create_lighter_http_timeout_error(msg: String) -> LighterHttpError {
86 LighterHttpError::Network(msg)
87}
88
89#[cfg(test)]
90mod tests {
91 use rstest::rstest;
92
93 use super::*;
94
95 #[rstest]
96 #[case::network_retries(LighterHttpError::Network("dns failure".into()), true)]
97 #[case::rate_limit_retries(LighterHttpError::RateLimit("429".into()), true)]
98 #[case::server_5xx_retries(LighterHttpError::Http { status: 503, body: "busy".into() }, true)]
99 #[case::server_500_retries(LighterHttpError::Http { status: 500, body: "boom".into() }, true)]
100 #[case::client_400_does_not_retry(LighterHttpError::Http { status: 400, body: "bad".into() }, false)]
101 #[case::client_404_does_not_retry(LighterHttpError::Http { status: 404, body: "missing".into() }, false)]
102 #[case::venue_does_not_retry(LighterHttpError::Venue { code: 20001, message: "invalid".into() }, false)]
103 #[case::incomplete_history_does_not_retry(
104 LighterHttpError::HistoryIncomplete {
105 data_type: "funding rate",
106 pages: 500,
107 },
108 false
109 )]
110 #[case::parse_does_not_retry(LighterHttpError::Parse("bad json".into()), false)]
111 fn test_should_retry_lighter_http_error(
112 #[case] error: LighterHttpError,
113 #[case] expected: bool,
114 ) {
115 assert_eq!(should_retry_lighter_http_error(&error), expected);
116 }
117}