1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use std::fmt::Display;

use longport_httpcli::HttpClientError;
use longport_wscli::WsClientError;

/// LongPort OpenAPI SDK error type
#[derive(Debug, thiserror::Error)]
pub enum Error {
    /// Decode Protobuf error
    #[error(transparent)]
    DecodeProtobuf(#[from] prost::DecodeError),

    /// Decode JSON error
    #[error(transparent)]
    DecodeJSON(#[from] serde_json::Error),

    /// Parse field
    #[error("parse field: {name}: {error}")]
    ParseField {
        /// Field name
        name: &'static str,

        /// Error detail
        error: String,
    },

    /// Unknown command
    #[error("unknown command: {0}")]
    UnknownCommand(
        /// Command code
        u8,
    ),

    /// Invalid security symbol
    #[error("invalid security symbol: {symbol}")]
    InvalidSecuritySymbol {
        /// Security symbol
        symbol: String,
    },

    /// HTTP client error
    #[error(transparent)]
    HttpClient(#[from] HttpClientError),

    /// Websocket client error
    #[error(transparent)]
    WsClient(#[from] WsClientError),

    /// Blocking error
    #[cfg(feature = "blocking")]
    #[error(transparent)]
    Blocking(#[from] crate::blocking::BlockingError),
}

impl Error {
    #[inline]
    pub(crate) fn parse_field_error(name: &'static str, error: impl Display) -> Self {
        Self::ParseField {
            name,
            error: error.to_string(),
        }
    }

    /// Consumes this error and returns a simple error
    pub fn into_simple_error(self) -> SimpleError {
        match self {
            Error::HttpClient(HttpClientError::OpenApi {
                code,
                message,
                trace_id,
            }) => SimpleError::Response {
                code: code as i64,
                message,
                trace_id,
            },
            Error::WsClient(WsClientError::ResponseError {
                detail: Some(detail),
                ..
            }) => SimpleError::Response {
                code: detail.code as i64,
                message: detail.msg,
                trace_id: String::new(),
            },
            Error::DecodeProtobuf(_)
            | Error::DecodeJSON(_)
            | Error::InvalidSecuritySymbol { .. }
            | Error::ParseField { .. }
            | Error::UnknownCommand(_)
            | Error::HttpClient(_)
            | Error::WsClient(_) => SimpleError::Other(self.to_string()),
            #[cfg(feature = "blocking")]
            Error::Blocking(_) => SimpleError::Other(self.to_string()),
        }
    }
}

/// LongPort OpenAPI SDK result type
pub type Result<T> = ::std::result::Result<T, Error>;

/// Simple error type
#[derive(Debug, thiserror::Error)]
pub enum SimpleError {
    /// Response error
    #[error("response error: code={code} message={message}")]
    Response {
        /// Error code
        code: i64,
        /// Error message
        message: String,
        /// Trace id
        trace_id: String,
    },
    /// Other error
    #[error("other error: {0}")]
    Other(String),
}

impl From<Error> for SimpleError {
    #[inline]
    fn from(err: Error) -> Self {
        err.into_simple_error()
    }
}

impl SimpleError {
    /// Returns the error code
    pub fn code(&self) -> Option<i64> {
        match self {
            SimpleError::Response { code, .. } => Some(*code),
            SimpleError::Other(_) => None,
        }
    }

    /// Returns the trace id
    pub fn trace_id(&self) -> Option<&str> {
        match self {
            SimpleError::Response { trace_id, .. } => Some(trace_id),
            SimpleError::Other(_) => None,
        }
    }

    /// Returns the error message
    pub fn message(&self) -> &str {
        match self {
            SimpleError::Response { message, .. } => message.as_str(),
            SimpleError::Other(message) => message.as_str(),
        }
    }
}