longport/
error.rs

1use std::fmt::Display;
2
3use longport_httpcli::HttpClientError;
4use longport_wscli::WsClientError;
5use time::OffsetDateTime;
6
7/// LongPort 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
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    /// Returns the OpenAPI error code
82    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    /// Consumes this error and returns a simple error
93    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
136/// LongPort OpenAPI SDK result type
137pub type Result<T> = ::std::result::Result<T, Error>;
138
139/// Simple error type
140#[derive(Debug, thiserror::Error)]
141pub enum SimpleError {
142    /// Http error
143    #[error("http error: status_code={status_code}")]
144    Http {
145        /// HTTP status code
146        status_code: u16,
147    },
148    /// OpenAPI error
149    #[error("openapi error: code={code} message={message}")]
150    OpenApi {
151        /// Error code
152        code: i64,
153        /// Error message
154        message: String,
155        /// Trace id
156        trace_id: String,
157    },
158    /// Other error
159    #[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/// Simple error kind
171#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172pub enum SimpleErrorKind {
173    /// HTTP error
174    Http,
175    /// OpenAPI error
176    OpenApi,
177    /// Other error
178    Other,
179}
180
181impl SimpleError {
182    /// Returns the kind of this error
183    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    /// Returns the error code
192    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    /// Returns the trace id
201    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    /// Returns the error message
210    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}