Skip to main content

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    /// A conversation event stream ended before a final result was observed
76    #[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    /// Returns the OpenAPI error code
90    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    /// Consumes this error and returns a simple error
101    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
146/// Longbridge OpenAPI SDK result type
147pub type Result<T> = ::std::result::Result<T, Error>;
148
149/// Simple error type
150#[derive(Debug, thiserror::Error)]
151pub enum SimpleError {
152    /// Http error
153    #[error("http error: status_code={status_code}")]
154    Http {
155        /// HTTP status code
156        status_code: u16,
157    },
158    /// OpenAPI error
159    #[error("openapi error: code={code} message={message}")]
160    OpenApi {
161        /// Error code
162        code: i64,
163        /// Error message
164        message: String,
165        /// Trace id
166        trace_id: String,
167    },
168    /// Other error
169    #[error("other error: {0}")]
170    Other(String),
171    /// OAuth error
172    #[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/// Simple error kind
184#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185pub enum SimpleErrorKind {
186    /// HTTP error
187    Http,
188    /// OpenAPI error
189    OpenApi,
190    /// Other error
191    Other,
192    /// OAuth error
193    OAuth,
194}
195
196impl SimpleError {
197    /// Returns the kind of this error
198    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    /// Returns the error code
208    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    /// Returns the trace id
218    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    /// Returns the error message
228    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}