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 Io(std::io::Error),
77}
78
79impl DeepSeekError {
80 pub(crate) fn api(error: ApiError, status: Option<u16>, body: Option<String>) -> Self {
81 DeepSeekError::Api {
82 error,
83 status,
84 body,
85 }
86 }
87
88 pub(crate) fn http(status: u16, body: String) -> Self {
89 DeepSeekError::Http {
90 status,
91 body: Some(body),
92 }
93 }
94
95 pub(crate) fn decode(message: String, body: String) -> Self {
96 DeepSeekError::Decode {
97 message,
98 body: Some(body),
99 }
100 }
101}
102
103impl fmt::Display for DeepSeekError {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 match self {
106 DeepSeekError::Api { error, status, .. } => write!(
107 f,
108 "DeepSeek API error: {} (type={}, param={:?}, code={:?}, status={:?})",
109 error.message, error.error_type, error.param, error.code, status
110 ),
111 DeepSeekError::Http { status, body } => {
112 write!(f, "HTTP error: status={}, body={:?}", status, body)
113 }
114 DeepSeekError::Decode { message, body } => {
115 write!(f, "Decode error: {} (body={:?})", message, body)
116 }
117 DeepSeekError::Transport(transport) => {
118 if let Some(kind) = &transport.kind {
119 write!(f, "reqwest {} error: {}", kind.as_str(), transport.source)
120 } else {
121 write!(f, "reqwest error: {}", transport.source)
122 }
123 }
124 DeepSeekError::Io(source) => write!(f, "IO error: {source}"),
125 }
126 }
127}
128
129impl Error for DeepSeekError {
130 fn source(&self) -> Option<&(dyn Error + 'static)> {
131 match self {
132 DeepSeekError::Transport(transport) => Some(&transport.source),
133 DeepSeekError::Io(source) => Some(source),
134 _ => None,
135 }
136 }
137}
138
139impl From<reqwest::Error> for DeepSeekError {
140 fn from(value: reqwest::Error) -> Self {
141 let kind = if value.is_decode() {
142 Some(ReqwestErrorKind::Decode)
143 } else if value.is_timeout() {
144 Some(ReqwestErrorKind::Timeout)
145 } else if value.is_connect() {
146 Some(ReqwestErrorKind::Connect)
147 } else if value.is_request() {
148 Some(ReqwestErrorKind::Request)
149 } else if value.is_body() {
150 Some(ReqwestErrorKind::Body)
151 } else if value.is_status() {
152 Some(ReqwestErrorKind::Status)
153 } else {
154 None
155 };
156
157 DeepSeekError::Transport(TransportError {
158 source: value,
159 kind,
160 })
161 }
162}
163
164impl From<std::io::Error> for DeepSeekError {
165 fn from(source: std::io::Error) -> Self {
166 DeepSeekError::Io(source)
167 }
168}