use std::fmt;
#[derive(Debug, Clone, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ApiErrorBody {
#[serde(default)]
pub error_type: Option<String>,
#[serde(default)]
pub error_code: Option<String>,
#[serde(default)]
pub error_message: Option<String>,
}
impl fmt::Display for ApiErrorBody {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[{}] {}: {}",
self.error_code.as_deref().unwrap_or("UNKNOWN"),
self.error_type.as_deref().unwrap_or("Unknown Error"),
self.error_message.as_deref().unwrap_or("No message"),
)
}
}
#[derive(Debug, thiserror::Error)]
pub enum DhanError {
#[error("API error: {0}")]
Api(ApiErrorBody),
#[error("HTTP {status}: {body}")]
HttpStatus {
status: reqwest::StatusCode,
body: String,
},
#[error("HTTP request failed: {0}")]
Http(#[from] reqwest::Error),
#[error("HTTP response body read failed after {status}: {source}")]
ResponseBody {
status: reqwest::StatusCode,
#[source]
source: reqwest::Error,
},
#[error("invalid HTTP header value: {0}")]
InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
#[error("JSON deserialization error: {0}")]
Json(#[from] serde_json::Error),
#[error("WebSocket error: {0}")]
WebSocket(Box<tokio_tungstenite::tungstenite::Error>),
#[error("URL error: {0}")]
Url(#[from] url::ParseError),
#[error("Invalid argument: {0}")]
InvalidArgument(String),
}
impl From<tokio_tungstenite::tungstenite::Error> for DhanError {
fn from(err: tokio_tungstenite::tungstenite::Error) -> Self {
DhanError::WebSocket(Box::new(err))
}
}
pub type Result<T> = std::result::Result<T, DhanError>;