1use std::fmt::Display;
2
3use longbridge_httpcli::HttpClientError;
4use longbridge_wscli::WsClientError;
5use time::OffsetDateTime;
6
7#[derive(Debug, thiserror::Error)]
9pub enum Error {
10 #[error(transparent)]
12 DecodeProtobuf(#[from] prost::DecodeError),
13
14 #[error(transparent)]
16 DecodeJSON(#[from] serde_json::Error),
17
18 #[error("parse field: {name}: {error}")]
20 ParseField {
21 name: &'static str,
23
24 error: String,
26 },
27
28 #[error("unknown command: {0}")]
30 UnknownCommand(
31 u8,
33 ),
34
35 #[error("invalid security symbol: {symbol}")]
37 InvalidSecuritySymbol {
38 symbol: String,
40 },
41
42 #[error("unknown market: {symbol}")]
44 UnknownMarket {
45 symbol: String,
47 },
48
49 #[error("unknown trade session: {symbol}, time={time}")]
51 UnknownTradeSession {
52 symbol: String,
54 time: OffsetDateTime,
56 },
57
58 #[error(transparent)]
60 HttpClient(#[from] HttpClientError),
61
62 #[error(transparent)]
64 WsClient(#[from] WsClientError),
65
66 #[cfg(feature = "blocking")]
68 #[error(transparent)]
69 Blocking(#[from] crate::blocking::BlockingError),
70
71 #[error("oauth error: {0}")]
73 OAuth(String),
74
75 #[error("conversation stream ended before a final result was observed")]
77 ConversationStreamEnded,
78}
79
80impl Error {
81 #[inline]
82 pub(crate) fn parse_field_error(name: &'static str, error: impl Display) -> Self {
83 Self::ParseField {
84 name,
85 error: error.to_string(),
86 }
87 }
88
89 pub fn openapi_error_code(&self) -> Option<i64> {
91 match self {
92 Error::HttpClient(HttpClientError::OpenApi { code, .. }) => Some(*code as i64),
93 Error::WsClient(WsClientError::ResponseError { detail, .. }) => {
94 detail.as_ref().map(|detail| detail.code as i64)
95 }
96 _ => None,
97 }
98 }
99
100 pub fn into_simple_error(self) -> SimpleError {
102 match self {
103 Error::HttpClient(HttpClientError::OpenApi {
104 code,
105 message,
106 trace_id,
107 }) => SimpleError::OpenApi {
108 code: code as i64,
109 message,
110 trace_id,
111 },
112 Error::HttpClient(HttpClientError::Http(err)) => {
113 if let Some(status) = err.0.status() {
114 SimpleError::Http {
115 status_code: status.as_u16(),
116 }
117 } else {
118 SimpleError::Other(err.to_string())
119 }
120 }
121 Error::WsClient(WsClientError::ResponseError {
122 detail: Some(detail),
123 ..
124 }) => SimpleError::OpenApi {
125 code: detail.code as i64,
126 message: detail.msg,
127 trace_id: String::new(),
128 },
129 Error::DecodeProtobuf(_)
130 | Error::DecodeJSON(_)
131 | Error::InvalidSecuritySymbol { .. }
132 | Error::UnknownMarket { .. }
133 | Error::UnknownTradeSession { .. }
134 | Error::ParseField { .. }
135 | Error::UnknownCommand(_)
136 | Error::HttpClient(_)
137 | Error::WsClient(_)
138 | Error::ConversationStreamEnded => SimpleError::Other(self.to_string()),
139 #[cfg(feature = "blocking")]
140 Error::Blocking(_) => SimpleError::Other(self.to_string()),
141 Error::OAuth(msg) => SimpleError::OAuth(msg),
142 }
143 }
144}
145
146pub type Result<T> = ::std::result::Result<T, Error>;
148
149#[derive(Debug, thiserror::Error)]
151pub enum SimpleError {
152 #[error("http error: status_code={status_code}")]
154 Http {
155 status_code: u16,
157 },
158 #[error("openapi error: code={code} message={message}")]
160 OpenApi {
161 code: i64,
163 message: String,
165 trace_id: String,
167 },
168 #[error("other error: {0}")]
170 Other(String),
171 #[error("oauth error: {0}")]
173 OAuth(String),
174}
175
176impl From<Error> for SimpleError {
177 #[inline]
178 fn from(err: Error) -> Self {
179 err.into_simple_error()
180 }
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185pub enum SimpleErrorKind {
186 Http,
188 OpenApi,
190 Other,
192 OAuth,
194}
195
196impl SimpleError {
197 pub fn kind(&self) -> SimpleErrorKind {
199 match self {
200 SimpleError::Http { .. } => SimpleErrorKind::Http,
201 SimpleError::OpenApi { .. } => SimpleErrorKind::OpenApi,
202 SimpleError::Other(_) => SimpleErrorKind::Other,
203 SimpleError::OAuth(_) => SimpleErrorKind::OAuth,
204 }
205 }
206
207 pub fn code(&self) -> Option<i64> {
209 match self {
210 SimpleError::Http { status_code } => Some(*status_code as i64),
211 SimpleError::OpenApi { code, .. } => Some(*code),
212 SimpleError::Other(_) => None,
213 SimpleError::OAuth(_) => None,
214 }
215 }
216
217 pub fn trace_id(&self) -> Option<&str> {
219 match self {
220 SimpleError::Http { .. } => None,
221 SimpleError::OpenApi { trace_id, .. } => Some(trace_id),
222 SimpleError::Other(_) => None,
223 SimpleError::OAuth(_) => None,
224 }
225 }
226
227 pub fn message(&self) -> &str {
229 match self {
230 SimpleError::Http { .. } => "bad status code",
231 SimpleError::OpenApi { message, .. } => message.as_str(),
232 SimpleError::Other(message) => message.as_str(),
233 SimpleError::OAuth(message) => message.as_str(),
234 }
235 }
236}