1use serde::Deserialize;
2use std::fmt;
3
4pub type Result<T> = std::result::Result<T, Error>;
6
7#[derive(Debug)]
9pub enum Error {
10 Api(ApiError),
12 Http(reqwest::Error),
14 Json(serde_json::Error),
16 WebSocket(tokio_tungstenite::tungstenite::Error),
18}
19
20impl fmt::Display for Error {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 match self {
23 Error::Api(e) => write!(f, "{e}"),
24 Error::Http(e) => write!(f, "qai: http error: {e}"),
25 Error::Json(e) => write!(f, "qai: json error: {e}"),
26 Error::WebSocket(e) => write!(f, "qai: websocket error: {e}"),
27 }
28 }
29}
30
31impl std::error::Error for Error {
32 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
33 match self {
34 Error::Api(_) => None,
35 Error::Http(e) => Some(e),
36 Error::Json(e) => Some(e),
37 Error::WebSocket(e) => Some(e),
38 }
39 }
40}
41
42impl From<tokio_tungstenite::tungstenite::Error> for Error {
43 fn from(err: tokio_tungstenite::tungstenite::Error) -> Self {
44 Error::WebSocket(err)
45 }
46}
47
48impl From<reqwest::Error> for Error {
49 fn from(err: reqwest::Error) -> Self {
50 Error::Http(err)
51 }
52}
53
54impl From<serde_json::Error> for Error {
55 fn from(err: serde_json::Error) -> Self {
56 Error::Json(err)
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct ApiError {
63 pub status_code: u16,
65 pub code: String,
67 pub message: String,
69 pub request_id: String,
71}
72
73impl fmt::Display for ApiError {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 if self.request_id.is_empty() {
76 write!(
77 f,
78 "qai: {} {}: {}",
79 self.status_code, self.code, self.message
80 )
81 } else {
82 write!(
83 f,
84 "qai: {} {}: {} (request_id={})",
85 self.status_code, self.code, self.message, self.request_id
86 )
87 }
88 }
89}
90
91impl std::error::Error for ApiError {}
92
93impl ApiError {
94 pub fn is_rate_limit(&self) -> bool {
96 self.status_code == 429
97 }
98
99 pub fn is_auth(&self) -> bool {
101 self.status_code == 401 || self.status_code == 403
102 }
103
104 pub fn is_not_found(&self) -> bool {
106 self.status_code == 404
107 }
108}
109
110pub fn is_rate_limit_error(err: &Error) -> bool {
112 matches!(err, Error::Api(e) if e.is_rate_limit())
113}
114
115pub fn is_auth_error(err: &Error) -> bool {
117 matches!(err, Error::Api(e) if e.is_auth())
118}
119
120pub fn is_not_found_error(err: &Error) -> bool {
122 matches!(err, Error::Api(e) if e.is_not_found())
123}
124
125#[derive(Deserialize)]
127pub(crate) struct ApiErrorBody {
128 pub error: ApiErrorInner,
129}
130
131#[derive(Deserialize)]
132pub(crate) struct ApiErrorInner {
133 #[serde(default)]
134 pub message: String,
135 #[serde(default)]
136 pub code: String,
137 #[serde(rename = "type", default)]
138 pub error_type: String,
139}