Skip to main content

deepseek_sdk/
error.rs

1//! Error types for DeepSeek API interactions.
2use serde::Deserialize;
3use std::error::Error;
4use std::fmt;
5
6/// Error payload returned by DeepSeek APIs.
7#[derive(Debug, Clone, Eq, PartialEq, Deserialize)]
8pub struct ApiError {
9    pub message: String,
10    #[serde(rename = "type")]
11    pub error_type: String,
12    /// Present only for API error payloads.
13    pub param: Option<String>,
14    /// Present only for API error payloads.
15    pub code: Option<String>,
16}
17
18/// Envelope used by some API endpoints: `{ "error": { ... } }`.
19#[derive(Debug, Clone, Eq, PartialEq, Deserialize)]
20pub(crate) struct ApiErrorEnvelope {
21    pub error: ApiError,
22}
23
24/// Categorized reqwest error kinds for diagnostics.
25#[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/// Transport-level failure from reqwest.
50#[derive(Debug)]
51pub struct TransportError {
52    pub source: reqwest::Error,
53    pub kind: Option<ReqwestErrorKind>,
54}
55
56/// Unified error type for this crate.
57#[non_exhaustive]
58#[derive(Debug)]
59pub enum DeepSeekError {
60    /// API returned a structured error payload.
61    Api {
62        error: ApiError,
63        status: Option<u16>,
64        body: Option<String>,
65    },
66    /// Non-JSON or otherwise unrecognized HTTP error.
67    Http { status: u16, body: Option<String> },
68    /// Response could not be decoded into the expected schema.
69    Decode {
70        message: String,
71        body: Option<String>,
72    },
73    /// Transport errors from reqwest.
74    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}