use serde::Deserialize;
use std::error::Error;
use std::fmt;
#[derive(Debug, Clone, Eq, PartialEq, Deserialize)]
pub struct ApiError {
pub message: String,
#[serde(rename = "type")]
pub error_type: String,
pub param: Option<String>,
pub code: Option<String>,
}
#[derive(Debug, Clone, Eq, PartialEq, Deserialize)]
pub(crate) struct ApiErrorEnvelope {
pub error: ApiError,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ReqwestErrorKind {
Decode,
Timeout,
Connect,
Request,
Body,
Status,
}
impl ReqwestErrorKind {
fn as_str(&self) -> &'static str {
match self {
ReqwestErrorKind::Decode => "decode",
ReqwestErrorKind::Timeout => "timeout",
ReqwestErrorKind::Connect => "connect",
ReqwestErrorKind::Request => "request",
ReqwestErrorKind::Body => "body",
ReqwestErrorKind::Status => "status",
}
}
}
#[derive(Debug)]
pub struct TransportError {
pub source: reqwest::Error,
pub kind: Option<ReqwestErrorKind>,
}
#[derive(Debug)]
pub enum DeepSeekError {
Api {
error: ApiError,
status: Option<u16>,
body: Option<String>,
},
Http { status: u16, body: Option<String> },
Decode {
message: String,
body: Option<String>,
},
Transport(TransportError),
}
impl DeepSeekError {
pub(crate) fn api(error: ApiError, status: Option<u16>, body: Option<String>) -> Self {
DeepSeekError::Api {
error,
status,
body,
}
}
pub(crate) fn http(status: u16, body: String) -> Self {
DeepSeekError::Http {
status,
body: Some(body),
}
}
pub(crate) fn decode(message: String, body: String) -> Self {
DeepSeekError::Decode {
message,
body: Some(body),
}
}
}
impl fmt::Display for DeepSeekError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DeepSeekError::Api { error, status, .. } => write!(
f,
"DeepSeek API error: {} (type={}, param={:?}, code={:?}, status={:?})",
error.message, error.error_type, error.param, error.code, status
),
DeepSeekError::Http { status, body } => {
write!(f, "HTTP error: status={}, body={:?}", status, body)
}
DeepSeekError::Decode { message, body } => {
write!(f, "Decode error: {} (body={:?})", message, body)
}
DeepSeekError::Transport(transport) => {
if let Some(kind) = &transport.kind {
write!(f, "reqwest {} error: {}", kind.as_str(), transport.source)
} else {
write!(f, "reqwest error: {}", transport.source)
}
}
}
}
}
impl Error for DeepSeekError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
DeepSeekError::Transport(transport) => Some(&transport.source),
_ => None,
}
}
}
impl From<reqwest::Error> for DeepSeekError {
fn from(value: reqwest::Error) -> Self {
let kind = if value.is_decode() {
Some(ReqwestErrorKind::Decode)
} else if value.is_timeout() {
Some(ReqwestErrorKind::Timeout)
} else if value.is_connect() {
Some(ReqwestErrorKind::Connect)
} else if value.is_request() {
Some(ReqwestErrorKind::Request)
} else if value.is_body() {
Some(ReqwestErrorKind::Body)
} else if value.is_status() {
Some(ReqwestErrorKind::Status)
} else {
None
};
DeepSeekError::Transport(TransportError {
source: value,
kind,
})
}
}