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#[derive(Debug, Clone, Eq, PartialEq)]
26pub enum ReqwestErrorKind {
27    Decode,
28    Timeout,
29    Connect,
30    Request,
31    Body,
32    Status,
33}
34
35impl ReqwestErrorKind {
36    fn as_str(&self) -> &'static str {
37        match self {
38            ReqwestErrorKind::Decode => "decode",
39            ReqwestErrorKind::Timeout => "timeout",
40            ReqwestErrorKind::Connect => "connect",
41            ReqwestErrorKind::Request => "request",
42            ReqwestErrorKind::Body => "body",
43            ReqwestErrorKind::Status => "status",
44        }
45    }
46}
47
48/// Transport-level failure from reqwest.
49#[derive(Debug)]
50pub struct TransportError {
51    pub source: reqwest::Error,
52    pub kind: Option<ReqwestErrorKind>,
53}
54
55/// Unified error type for this crate.
56#[derive(Debug)]
57pub enum DeepSeekError {
58    /// API returned a structured error payload.
59    Api {
60        error: ApiError,
61        status: Option<u16>,
62        body: Option<String>,
63    },
64    /// Non-JSON or otherwise unrecognized HTTP error.
65    Http { status: u16, body: Option<String> },
66    /// Response could not be decoded into the expected schema.
67    Decode {
68        message: String,
69        body: Option<String>,
70    },
71    /// Transport errors from reqwest.
72    Transport(TransportError),
73}
74
75impl DeepSeekError {
76    pub(crate) fn api(error: ApiError, status: Option<u16>, body: Option<String>) -> Self {
77        DeepSeekError::Api {
78            error,
79            status,
80            body,
81        }
82    }
83
84    pub(crate) fn http(status: u16, body: String) -> Self {
85        DeepSeekError::Http {
86            status,
87            body: Some(body),
88        }
89    }
90
91    pub(crate) fn decode(message: String, body: String) -> Self {
92        DeepSeekError::Decode {
93            message,
94            body: Some(body),
95        }
96    }
97}
98
99impl fmt::Display for DeepSeekError {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        match self {
102            DeepSeekError::Api { error, status, .. } => write!(
103                f,
104                "DeepSeek API error: {} (type={}, param={:?}, code={:?}, status={:?})",
105                error.message, error.error_type, error.param, error.code, status
106            ),
107            DeepSeekError::Http { status, body } => {
108                write!(f, "HTTP error: status={}, body={:?}", status, body)
109            }
110            DeepSeekError::Decode { message, body } => {
111                write!(f, "Decode error: {} (body={:?})", message, body)
112            }
113            DeepSeekError::Transport(transport) => {
114                if let Some(kind) = &transport.kind {
115                    write!(f, "reqwest {} error: {}", kind.as_str(), transport.source)
116                } else {
117                    write!(f, "reqwest error: {}", transport.source)
118                }
119            }
120        }
121    }
122}
123
124impl Error for DeepSeekError {
125    fn source(&self) -> Option<&(dyn Error + 'static)> {
126        match self {
127            DeepSeekError::Transport(transport) => Some(&transport.source),
128            _ => None,
129        }
130    }
131}
132
133impl From<reqwest::Error> for DeepSeekError {
134    fn from(value: reqwest::Error) -> Self {
135        let kind = if value.is_decode() {
136            Some(ReqwestErrorKind::Decode)
137        } else if value.is_timeout() {
138            Some(ReqwestErrorKind::Timeout)
139        } else if value.is_connect() {
140            Some(ReqwestErrorKind::Connect)
141        } else if value.is_request() {
142            Some(ReqwestErrorKind::Request)
143        } else if value.is_body() {
144            Some(ReqwestErrorKind::Body)
145        } else if value.is_status() {
146            Some(ReqwestErrorKind::Status)
147        } else {
148            None
149        };
150
151        DeepSeekError::Transport(TransportError {
152            source: value,
153            kind,
154        })
155    }
156}