use nautilus_network::http::HttpClientError;
use serde_json::Value;
use thiserror::Error;
use crate::signing::auth::AuthError;
pub type Result<T> = std::result::Result<T, DeriveHttpError>;
#[derive(Debug, Error)]
pub enum DeriveHttpError {
#[error("transport error: {0}")]
Transport(String),
#[error("HTTP {status}: {message}")]
Http {
status: u16,
message: String,
},
#[error("JSON-RPC error {code}: {message}")]
JsonRpc {
code: i64,
message: String,
data: Option<Value>,
},
#[error("missing `result` in JSON-RPC response for `{method}`")]
MissingResult {
method: String,
},
#[error("decode error: {0}")]
Decode(String),
#[error("serde error: {0}")]
Serde(#[from] serde_json::Error),
#[error("auth error: {0}")]
Auth(#[from] AuthError),
#[error("missing credentials for private endpoint `{method}`")]
MissingCredentials {
method: String,
},
}
impl DeriveHttpError {
#[must_use]
pub fn transport(msg: impl Into<String>) -> Self {
Self::Transport(msg.into())
}
#[must_use]
pub fn http(status: u16, message: impl Into<String>) -> Self {
Self::Http {
status,
message: message.into(),
}
}
#[must_use]
pub fn decode(msg: impl Into<String>) -> Self {
Self::Decode(msg.into())
}
#[must_use]
pub fn is_transport_error(&self) -> bool {
matches!(self, Self::Transport(_))
}
}
impl From<HttpClientError> for DeriveHttpError {
fn from(value: HttpClientError) -> Self {
Self::Transport(value.to_string())
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use serde_json::json;
use super::*;
#[rstest]
fn test_transport_is_transport_error() {
assert!(DeriveHttpError::transport("conn reset").is_transport_error());
}
#[rstest]
fn test_http_is_not_transport_error() {
assert!(!DeriveHttpError::http(503, "service unavailable").is_transport_error());
}
#[rstest]
fn test_jsonrpc_error_carries_code_and_data() {
let err = DeriveHttpError::JsonRpc {
code: -32602,
message: "Invalid params".to_string(),
data: Some(json!({"field": "currency"})),
};
let text = err.to_string();
assert!(text.contains("-32602"));
assert!(text.contains("Invalid params"));
assert!(!err.is_transport_error());
}
#[rstest]
fn test_missing_credentials_names_method() {
let err = DeriveHttpError::MissingCredentials {
method: "private/order".to_string(),
};
assert!(err.to_string().contains("private/order"));
}
#[rstest]
fn test_http_client_error_maps_to_transport() {
let upstream = HttpClientError::Error("boom".to_string());
let mapped: DeriveHttpError = upstream.into();
assert!(mapped.is_transport_error());
}
}