1use serde::Deserialize;
3use std::error::Error;
4use std::fmt;
5
6#[derive(Debug, Clone, Eq, PartialEq, Deserialize)]
8pub struct ApiError {
9 pub message: String,
10 #[serde(rename = "type")]
11 pub error_type: String,
12 pub param: Option<String>,
14 pub code: Option<String>,
16}
17
18#[derive(Debug, Clone, Eq, PartialEq, Deserialize)]
20pub(crate) struct ApiErrorEnvelope {
21 pub error: ApiError,
22}
23
24#[non_exhaustive]
26#[derive(Debug, Clone, Eq, PartialEq)]
27pub enum ReqwestErrorKind {
28 Decode,
29 Timeout,
30 Connect,
31 Request,
32 Body,
33 Status,
34}
35
36impl ReqwestErrorKind {
37 fn as_str(&self) -> &'static str {
38 match self {
39 ReqwestErrorKind::Decode => "decode",
40 ReqwestErrorKind::Timeout => "timeout",
41 ReqwestErrorKind::Connect => "connect",
42 ReqwestErrorKind::Request => "request",
43 ReqwestErrorKind::Body => "body",
44 ReqwestErrorKind::Status => "status",
45 }
46 }
47}
48
49#[derive(Debug)]
51pub struct TransportError {
52 pub source: reqwest::Error,
53 pub kind: Option<ReqwestErrorKind>,
54}
55
56#[non_exhaustive]
58#[derive(Debug)]
59pub enum DeepSeekError {
60 Api {
62 error: ApiError,
63 status: Option<u16>,
64 body: Option<String>,
65 },
66 Http { status: u16, body: Option<String> },
68 Decode {
70 message: String,
71 body: Option<String>,
72 },
73 Transport(TransportError),
75}
76
77impl DeepSeekError {
78 pub(crate) fn api(error: ApiError, status: Option<u16>, body: Option<String>) -> Self {
79 DeepSeekError::Api {
80 error,
81 status,
82 body,
83 }
84 }
85
86 pub(crate) fn http(status: u16, body: String) -> Self {
87 DeepSeekError::Http {
88 status,
89 body: Some(body),
90 }
91 }
92
93 pub(crate) fn decode(message: String, body: String) -> Self {
94 DeepSeekError::Decode {
95 message,
96 body: Some(body),
97 }
98 }
99}
100
101impl fmt::Display for DeepSeekError {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 DeepSeekError::Api { error, status, .. } => write!(
105 f,
106 "DeepSeek API error: {} (type={}, param={:?}, code={:?}, status={:?})",
107 error.message, error.error_type, error.param, error.code, status
108 ),
109 DeepSeekError::Http { status, body } => {
110 write!(f, "HTTP error: status={}, body={:?}", status, body)
111 }
112 DeepSeekError::Decode { message, body } => {
113 write!(f, "Decode error: {} (body={:?})", message, body)
114 }
115 DeepSeekError::Transport(transport) => {
116 if let Some(kind) = &transport.kind {
117 write!(f, "reqwest {} error: {}", kind.as_str(), transport.source)
118 } else {
119 write!(f, "reqwest error: {}", transport.source)
120 }
121 }
122 }
123 }
124}
125
126impl Error for DeepSeekError {
127 fn source(&self) -> Option<&(dyn Error + 'static)> {
128 match self {
129 DeepSeekError::Transport(transport) => Some(&transport.source),
130 _ => None,
131 }
132 }
133}
134
135impl From<reqwest::Error> for DeepSeekError {
136 fn from(value: reqwest::Error) -> Self {
137 let kind = if value.is_decode() {
138 Some(ReqwestErrorKind::Decode)
139 } else if value.is_timeout() {
140 Some(ReqwestErrorKind::Timeout)
141 } else if value.is_connect() {
142 Some(ReqwestErrorKind::Connect)
143 } else if value.is_request() {
144 Some(ReqwestErrorKind::Request)
145 } else if value.is_body() {
146 Some(ReqwestErrorKind::Body)
147 } else if value.is_status() {
148 Some(ReqwestErrorKind::Status)
149 } else {
150 None
151 };
152
153 DeepSeekError::Transport(TransportError {
154 source: value,
155 kind,
156 })
157 }
158}