use std::{error::Error, fmt::Write, time::Duration};
use simulator_api::{BacktestError, BacktestResponse};
use thiserror::Error;
use tokio_tungstenite::tungstenite;
pub type BacktestClientResult<T> = Result<T, BacktestClientError>;
pub(crate) fn err_chain(e: &(dyn Error + 'static)) -> String {
let mut out = e.to_string();
let mut src = e.source();
while let Some(s) = src {
let _ = write!(&mut out, ": {s}");
src = s.source();
}
out
}
#[derive(Debug, Error)]
pub enum BacktestClientError {
#[error("invalid params: {message}")]
InvalidParams { message: String },
#[error("invalid API key header value: {0}")]
InvalidApiKeyHeader(#[from] tungstenite::http::header::InvalidHeaderValue),
#[error("failed to build websocket request for {url}: {source}")]
BuildRequest {
url: String,
#[source]
source: Box<tungstenite::Error>,
},
#[error("websocket connect to {url} failed: {source}")]
Connect {
url: String,
#[source]
source: Box<tungstenite::Error>,
},
#[error("timeout while {action} after {duration:?}")]
Timeout {
action: &'static str,
duration: Duration,
},
#[error("failed to serialize request: {source}")]
SerializeRequest {
#[source]
source: serde_json::Error,
},
#[error("failed to deserialize response: {source}; raw={raw}")]
DeserializeResponse {
raw: String,
#[source]
source: serde_json::Error,
},
#[error("remote error: {0}")]
Remote(#[from] BacktestError),
#[error("websocket closed: {reason}")]
Closed { reason: String },
#[error("unexpected response while {context}: {response:?}")]
UnexpectedResponse {
context: &'static str,
response: Box<BacktestResponse>,
},
#[error("websocket error while {action}: {source}")]
WebSocket {
action: &'static str,
#[source]
source: Box<tungstenite::Error>,
},
#[error("HTTP request to {url} failed: {source}")]
Http {
url: String,
#[source]
source: Box<dyn std::error::Error + Send + Sync>,
},
#[error("HTTP {status} from {url}: {body}")]
HttpStatus {
url: String,
status: reqwest::StatusCode,
body: String,
},
}