1use std::fmt::Display;
2
3use longport_httpcli::HttpClientError;
4use longport_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
72impl Error {
73 #[inline]
74 pub(crate) fn parse_field_error(name: &'static str, error: impl Display) -> Self {
75 Self::ParseField {
76 name,
77 error: error.to_string(),
78 }
79 }
80
81 pub fn openapi_error_code(&self) -> Option<i64> {
83 match self {
84 Error::HttpClient(HttpClientError::OpenApi { code, .. }) => Some(*code as i64),
85 Error::WsClient(WsClientError::ResponseError { detail, .. }) => {
86 detail.as_ref().map(|detail| detail.code as i64)
87 }
88 _ => None,
89 }
90 }
91
92 pub fn into_simple_error(self) -> SimpleError {
94 match self {
95 Error::HttpClient(HttpClientError::OpenApi {
96 code,
97 message,
98 trace_id,
99 }) => SimpleError::OpenApi {
100 code: code as i64,
101 message,
102 trace_id,
103 },
104 Error::HttpClient(HttpClientError::Http(err)) => {
105 if let Some(status) = err.0.status() {
106 SimpleError::Http {
107 status_code: status.as_u16(),
108 }
109 } else {
110 SimpleError::Other(err.to_string())
111 }
112 }
113 Error::WsClient(WsClientError::ResponseError {
114 detail: Some(detail),
115 ..
116 }) => SimpleError::OpenApi {
117 code: detail.code as i64,
118 message: detail.msg,
119 trace_id: String::new(),
120 },
121 Error::DecodeProtobuf(_)
122 | Error::DecodeJSON(_)
123 | Error::InvalidSecuritySymbol { .. }
124 | Error::UnknownMarket { .. }
125 | Error::UnknownTradeSession { .. }
126 | Error::ParseField { .. }
127 | Error::UnknownCommand(_)
128 | Error::HttpClient(_)
129 | Error::WsClient(_) => SimpleError::Other(self.to_string()),
130 #[cfg(feature = "blocking")]
131 Error::Blocking(_) => SimpleError::Other(self.to_string()),
132 }
133 }
134}
135
136pub type Result<T> = ::std::result::Result<T, Error>;
138
139#[derive(Debug, thiserror::Error)]
141pub enum SimpleError {
142 #[error("http error: status_code={status_code}")]
144 Http {
145 status_code: u16,
147 },
148 #[error("openapi error: code={code} message={message}")]
150 OpenApi {
151 code: i64,
153 message: String,
155 trace_id: String,
157 },
158 #[error("other error: {0}")]
160 Other(String),
161}
162
163impl From<Error> for SimpleError {
164 #[inline]
165 fn from(err: Error) -> Self {
166 err.into_simple_error()
167 }
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172pub enum SimpleErrorKind {
173 Http,
175 OpenApi,
177 Other,
179}
180
181impl SimpleError {
182 pub fn kind(&self) -> SimpleErrorKind {
184 match self {
185 SimpleError::Http { .. } => SimpleErrorKind::Http,
186 SimpleError::OpenApi { .. } => SimpleErrorKind::OpenApi,
187 SimpleError::Other(_) => SimpleErrorKind::Other,
188 }
189 }
190
191 pub fn code(&self) -> Option<i64> {
193 match self {
194 SimpleError::Http { status_code } => Some(*status_code as i64),
195 SimpleError::OpenApi { code, .. } => Some(*code),
196 SimpleError::Other(_) => None,
197 }
198 }
199
200 pub fn trace_id(&self) -> Option<&str> {
202 match self {
203 SimpleError::Http { .. } => None,
204 SimpleError::OpenApi { trace_id, .. } => Some(trace_id),
205 SimpleError::Other(_) => None,
206 }
207 }
208
209 pub fn message(&self) -> &str {
211 match self {
212 SimpleError::Http { .. } => "bad status code",
213 SimpleError::OpenApi { message, .. } => message.as_str(),
214 SimpleError::Other(message) => message.as_str(),
215 }
216 }
217}