nautilus_deribit/http/
error.rs1use std::fmt;
19
20use crate::common::consts::should_retry_error_code;
21
22#[derive(Debug, Clone)]
24pub enum DeribitHttpError {
25 MissingCredentials,
27 DeribitError { error_code: i64, message: String },
29 JsonError(String),
31 ValidationError(String),
33 NetworkError(String),
35 Timeout(String),
37 Canceled(String),
39 UnexpectedStatus { status: u16, body: String },
41}
42
43impl fmt::Display for DeribitHttpError {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 match self {
46 Self::MissingCredentials => write!(f, "Missing API credentials"),
47 Self::DeribitError {
48 error_code,
49 message,
50 } => write!(f, "Deribit error {error_code}: {message}"),
51 Self::JsonError(msg) => write!(f, "JSON error: {msg}"),
52 Self::ValidationError(msg) => write!(f, "Validation error: {msg}"),
53 Self::NetworkError(msg) => write!(f, "Network error: {msg}"),
54 Self::Timeout(msg) => write!(f, "Timeout: {msg}"),
55 Self::Canceled(msg) => write!(f, "Canceled: {msg}"),
56 Self::UnexpectedStatus { status, body } => {
57 write!(f, "Unexpected status {status}: {body}")
58 }
59 }
60 }
61}
62
63impl std::error::Error for DeribitHttpError {}
64
65impl From<serde_json::Error> for DeribitHttpError {
66 fn from(error: serde_json::Error) -> Self {
67 Self::JsonError(error.to_string())
68 }
69}
70
71impl From<anyhow::Error> for DeribitHttpError {
72 fn from(error: anyhow::Error) -> Self {
73 Self::NetworkError(error.to_string())
74 }
75}
76
77impl DeribitHttpError {
78 #[must_use]
80 pub fn is_retryable(&self) -> bool {
81 match self {
82 Self::NetworkError(_) => true,
83 Self::UnexpectedStatus { status, .. } => *status >= 500 || *status == 429,
84 Self::DeribitError { error_code, .. } => should_retry_error_code(*error_code),
85 _ => false,
86 }
87 }
88
89 pub fn from_jsonrpc_error(
100 error_code: i64,
101 message: String,
102 data: Option<&serde_json::Value>,
103 ) -> Self {
104 match error_code {
105 -32700 => Self::ValidationError(format!("Parse error: {message}")),
107 -32600 => Self::ValidationError(format!("Invalid request: {message}")),
108 -32601 => Self::ValidationError(format!("Method not found: {message}")),
109 -32602 => {
110 let detail = data
112 .and_then(|d| d.as_object())
113 .and_then(|obj| {
114 let param = obj.get("param")?.as_str()?;
115 let reason = obj.get("reason")?.as_str()?;
116 Some(format!(" (parameter '{param}': {reason})"))
117 })
118 .unwrap_or_default();
119 Self::ValidationError(format!("Invalid params: {message}{detail}"))
120 }
121 -32603 => Self::ValidationError(format!("Internal error: {message}")),
122 _ => Self::DeribitError {
124 error_code,
125 message,
126 },
127 }
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use rstest::rstest;
134
135 use super::*;
136
137 #[rstest]
138 #[case(DeribitHttpError::NetworkError("timeout".to_string()), true)]
139 #[case(DeribitHttpError::Timeout("read".to_string()), false)]
140 #[case(DeribitHttpError::UnexpectedStatus { status: 500, body: String::new() }, true)]
141 #[case(DeribitHttpError::UnexpectedStatus { status: 502, body: String::new() }, true)]
142 #[case(DeribitHttpError::UnexpectedStatus { status: 429, body: String::new() }, true)]
143 #[case(DeribitHttpError::UnexpectedStatus { status: 403, body: String::new() }, false)]
144 #[case(DeribitHttpError::DeribitError { error_code: 10028, message: String::new() }, true)]
145 #[case(DeribitHttpError::DeribitError { error_code: 13888, message: String::new() }, true)]
146 #[case(DeribitHttpError::DeribitError { error_code: -32600, message: String::new() }, false)]
147 #[case(DeribitHttpError::JsonError("bad".to_string()), false)]
148 #[case(DeribitHttpError::ValidationError("bad".to_string()), false)]
149 #[case(DeribitHttpError::MissingCredentials, false)]
150 #[case(DeribitHttpError::Canceled("shutdown".to_string()), false)]
151 fn test_is_retryable(#[case] error: DeribitHttpError, #[case] expected: bool) {
152 assert_eq!(error.is_retryable(), expected);
153 }
154}