longbridge/
error.rs

1use std::fmt::Display;
2
3use longbridge_httpcli::HttpClientError;
4use longbridge_wscli::WsClientError;
5use time::OffsetDateTime;
6
7/// Longbridge OpenAPI SDK error type
8#[derive(Debug, thiserror::Error)]
9pub enum Error {
10    /// Decode Protobuf error
11    #[error(transparent)]
12    DecodeProtobuf(#[from] prost::DecodeError),
13
14    /// Decode JSON error
15    #[error(transparent)]
16    DecodeJSON(#[from] serde_json::Error),
17
18    /// Parse field
19    #[error("parse field: {name}: {error}")]
20    ParseField {
21        /// Field name
22        name: &'static str,
23
24        /// Error detail
25        error: String,
26    },
27
28    /// Unknown command
29    #[error("unknown command: {0}")]
30    UnknownCommand(
31        /// Command code
32        u8,
33    ),
34
35    /// Invalid security symbol
36    #[error("invalid security symbol: {symbol}")]
37    InvalidSecuritySymbol {
38        /// Security symbol
39        symbol: String,
40    },
41
42    /// Unknown market
43    #[error("unknown market: {symbol}")]
44    UnknownMarket {
45        /// Security symbol
46        symbol: String,
47    },
48
49    /// Unknown trade session
50    #[error("unknown trade session: {symbol}, time={time}")]
51    UnknownTradeSession {
52        /// Security symbol
53        symbol: String,
54        /// time
55        time: OffsetDateTime,
56    },
57
58    /// HTTP client error
59    #[error(transparent)]
60    HttpClient(#[from] HttpClientError),
61
62    /// Websocket client error
63    #[error(transparent)]
64    WsClient(#[from] WsClientError),
65
66    /// Blocking error
67    #[cfg(feature = "blocking")]
68    #[error(transparent)]
69    Blocking(#[from] crate::blocking::BlockingError),
70
71    /// OAuth error
72    #[error("oauth error: {0}")]
73    OAuth(String),
74}
75
76impl Error {
77    #[inline]
78    pub(crate) fn parse_field_error(name: &'static str, error: impl Display) -> Self {
79        Self::ParseField {
80            name,
81            error: error.to_string(),
82        }
83    }
84
85    /// Returns the OpenAPI error code
86    pub fn openapi_error_code(&self) -> Option<i64> {
87        match self {
88            Error::HttpClient(HttpClientError::OpenApi { code, .. }) => Some(*code as i64),
89            Error::WsClient(WsClientError::ResponseError { detail, .. }) => {
90                detail.as_ref().map(|detail| detail.code as i64)
91            }
92            _ => None,
93        }
94    }
95
96    /// Consumes this error and returns a simple error
97    pub fn into_simple_error(self) -> SimpleError {
98        match self {
99            Error::HttpClient(HttpClientError::OpenApi {
100                code,
101                message,
102                trace_id,
103            }) => SimpleError::OpenApi {
104                code: code as i64,
105                message,
106                trace_id,
107            },
108            Error::HttpClient(HttpClientError::Http(err)) => {
109                if let Some(status) = err.0.status() {
110                    SimpleError::Http {
111                        status_code: status.as_u16(),
112                    }
113                } else {
114                    SimpleError::Other(err.to_string())
115                }
116            }
117            Error::WsClient(WsClientError::ResponseError {
118                detail: Some(detail),
119                ..
120            }) => SimpleError::OpenApi {
121                code: detail.code as i64,
122                message: detail.msg,
123                trace_id: String::new(),
124            },
125            Error::DecodeProtobuf(_)
126            | Error::DecodeJSON(_)
127            | Error::InvalidSecuritySymbol { .. }
128            | Error::UnknownMarket { .. }
129            | Error::UnknownTradeSession { .. }
130            | Error::ParseField { .. }
131            | Error::UnknownCommand(_)
132            | Error::HttpClient(_)
133            | Error::WsClient(_) => SimpleError::Other(self.to_string()),
134            #[cfg(feature = "blocking")]
135            Error::Blocking(_) => SimpleError::Other(self.to_string()),
136            Error::OAuth(msg) => SimpleError::OAuth(msg),
137        }
138    }
139}
140
141/// Longbridge OpenAPI SDK result type
142pub type Result<T> = ::std::result::Result<T, Error>;
143
144/// Simple error type
145#[derive(Debug, thiserror::Error)]
146pub enum SimpleError {
147    /// Http error
148    #[error("http error: status_code={status_code}")]
149    Http {
150        /// HTTP status code
151        status_code: u16,
152    },
153    /// OpenAPI error
154    #[error("openapi error: code={code} message={message}")]
155    OpenApi {
156        /// Error code
157        code: i64,
158        /// Error message
159        message: String,
160        /// Trace id
161        trace_id: String,
162    },
163    /// Other error
164    #[error("other error: {0}")]
165    Other(String),
166    /// OAuth error
167    #[error("oauth error: {0}")]
168    OAuth(String),
169}
170
171impl From<Error> for SimpleError {
172    #[inline]
173    fn from(err: Error) -> Self {
174        err.into_simple_error()
175    }
176}
177
178/// Simple error kind
179#[derive(Debug, Clone, Copy, PartialEq, Eq)]
180pub enum SimpleErrorKind {
181    /// HTTP error
182    Http,
183    /// OpenAPI error
184    OpenApi,
185    /// Other error
186    Other,
187    /// OAuth error
188    OAuth,
189}
190
191impl SimpleError {
192    /// Returns the kind of this error
193    pub fn kind(&self) -> SimpleErrorKind {
194        match self {
195            SimpleError::Http { .. } => SimpleErrorKind::Http,
196            SimpleError::OpenApi { .. } => SimpleErrorKind::OpenApi,
197            SimpleError::Other(_) => SimpleErrorKind::Other,
198            SimpleError::OAuth(_) => SimpleErrorKind::OAuth,
199        }
200    }
201
202    /// Returns the error code
203    pub fn code(&self) -> Option<i64> {
204        match self {
205            SimpleError::Http { status_code } => Some(*status_code as i64),
206            SimpleError::OpenApi { code, .. } => Some(*code),
207            SimpleError::Other(_) => None,
208            SimpleError::OAuth(_) => None,
209        }
210    }
211
212    /// Returns the trace id
213    pub fn trace_id(&self) -> Option<&str> {
214        match self {
215            SimpleError::Http { .. } => None,
216            SimpleError::OpenApi { trace_id, .. } => Some(trace_id),
217            SimpleError::Other(_) => None,
218            SimpleError::OAuth(_) => None,
219        }
220    }
221
222    /// Returns the error message
223    pub fn message(&self) -> &str {
224        match self {
225            SimpleError::Http { .. } => "bad status code",
226            SimpleError::OpenApi { message, .. } => message.as_str(),
227            SimpleError::Other(message) => message.as_str(),
228            SimpleError::OAuth(message) => message.as_str(),
229        }
230    }
231}