kraken_async_rs/clients/
errors.rs

1//! Client error type and sub-types
2use hyper::http::uri::InvalidUri;
3use hyper::http::Error as HyperHttpError;
4use hyper::Error as HyperError;
5use hyper_util::client::legacy::Error as HyperClientError;
6use serde::Deserialize;
7use serde_json::Error as SerdeError;
8use std::error::Error;
9use std::fmt::{Display, Formatter};
10use url::ParseError as UrlParseError;
11
12/// `ClientError::Kraken` contains all parsed error messages like `PermissionDenied` and
13/// `UnknownMethod`, but is not inclusive of all potential error messages that can be present in
14/// the `error` field of a response.
15///
16/// Various dependency errors are exposed, but may be of limited use aside from triggering retries.
17#[derive(Debug)]
18pub enum ClientError {
19    Serde(SerdeError),
20    Hyper(HyperError),
21    HyperClient(HyperClientError),
22    HyperHttp(HyperHttpError),
23    HyperUri(InvalidUri),
24    HttpStatus(String),
25    UrlParse(UrlParseError),
26    Parse(&'static str),
27    Kraken(KrakenError),
28}
29
30impl From<HyperError> for ClientError {
31    fn from(value: HyperError) -> Self {
32        Self::Hyper(value)
33    }
34}
35
36impl From<HyperHttpError> for ClientError {
37    fn from(value: HyperHttpError) -> Self {
38        Self::HyperHttp(value)
39    }
40}
41
42impl From<HyperClientError> for ClientError {
43    fn from(value: HyperClientError) -> Self {
44        Self::HyperClient(value)
45    }
46}
47
48impl From<UrlParseError> for ClientError {
49    fn from(value: UrlParseError) -> Self {
50        Self::UrlParse(value)
51    }
52}
53
54impl From<InvalidUri> for ClientError {
55    fn from(value: InvalidUri) -> Self {
56        Self::HyperUri(value)
57    }
58}
59
60impl From<SerdeError> for ClientError {
61    fn from(value: SerdeError) -> Self {
62        Self::Serde(value)
63    }
64}
65
66impl Display for ClientError {
67    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
68        match self {
69            ClientError::Serde(err) => write!(f, "{}", err),
70            ClientError::Hyper(err) => write!(f, "{}", err),
71            ClientError::HyperClient(err) => write!(f, "{}", err),
72            ClientError::HyperHttp(err) => write!(f, "{}", err),
73            ClientError::HyperUri(err) => write!(f, "{}", err),
74            ClientError::HttpStatus(body) => write!(f, "Non-successful status with body: {}", body),
75            ClientError::UrlParse(err) => write!(f, "{}", err),
76            ClientError::Parse(err) => write!(f, "{}", err),
77            ClientError::Kraken(err) => write!(f, "{}", err),
78        }
79    }
80}
81
82impl Error for ClientError {
83    fn source(&self) -> Option<&(dyn Error + 'static)> {
84        match self {
85            ClientError::Serde(e) => Some(e),
86            ClientError::Hyper(e) => Some(e),
87            ClientError::HyperClient(e) => Some(e),
88            ClientError::HyperHttp(e) => Some(e),
89            ClientError::HyperUri(e) => Some(e),
90            ClientError::HttpStatus(_) => None,
91            ClientError::UrlParse(e) => Some(e),
92            ClientError::Parse(_) => None,
93            ClientError::Kraken(e) => Some(e),
94        }
95    }
96}
97
98/// Enum of all parsed Kraken errors.
99///
100/// The user still has significant responsibilities in checking error fields!
101///
102/// Parsing of error messages is done for the general and higher-level errors produced by the API,
103/// not including individual trading and request errors. For instance, `PermissionDenied` and
104/// `UnknownAssetPair` are parsed because they're broadly applicable to many endpoints, while specific
105/// trading errors like "InsufficientMargin" are not.
106///
107/// More documentation can be found on Kraken's [API error support page].
108///
109/// [API error support page]: https://support.kraken.com/hc/en-us/articles/360001491786-API-error-messages
110#[derive(Debug, Deserialize, Eq, PartialEq, Clone)]
111pub enum KrakenError {
112    PermissionDenied,
113    InvalidKey,
114    UnknownAssetPair,
115    InvalidArguments(String),
116    InvalidSignature,
117    InvalidNonce,
118    InvalidSession,
119    BadRequest,
120    UnknownMethod,
121    RateLimitExceeded,
122    TradingRateLimitExceeded,
123    TemporaryLockout,
124    ServiceUnavailable,
125    ServiceBusy,
126    InternalError,
127    TradeLocked,
128    FeatureDisabled,
129}
130
131impl Error for KrakenError {}
132
133impl Display for KrakenError {
134    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
135        match self {
136            KrakenError::PermissionDenied => write!(f, "PermissionDenied"),
137            KrakenError::InvalidKey => write!(f, "InvalidKey"),
138            KrakenError::UnknownAssetPair => write!(f, "UnknownAssetPair"),
139            KrakenError::InvalidArguments(s) => write!(f, "{s}"),
140            KrakenError::InvalidSignature => write!(f, "InvalidSignature"),
141            KrakenError::InvalidNonce => write!(f, "InvalidNonce"),
142            KrakenError::InvalidSession => write!(f, "InvalidSession"),
143            KrakenError::BadRequest => write!(f, "BadRequest"),
144            KrakenError::UnknownMethod => write!(f, "UnknownMethod"),
145            KrakenError::RateLimitExceeded => write!(f, "RateLimitExceeded"),
146            KrakenError::TradingRateLimitExceeded => write!(f, "TradingRateLimitExceeded"),
147            KrakenError::TemporaryLockout => write!(f, "TemporaryLockout"),
148            KrakenError::ServiceUnavailable => write!(f, "ServiceUnavailable"),
149            KrakenError::ServiceBusy => write!(f, "ServiceBusy"),
150            KrakenError::InternalError => write!(f, "InternalError"),
151            KrakenError::TradeLocked => write!(f, "TradeLocked"),
152            KrakenError::FeatureDisabled => write!(f, "FeatureDisabled"),
153        }
154    }
155}
156
157/// Parsing for all supported error types from the raw messages in `ResultErrorResponse.error`.
158impl TryFrom<&String> for KrakenError {
159    type Error = ();
160
161    fn try_from(value: &String) -> Result<Self, Self::Error> {
162        if value.starts_with("EGeneral:Permission denied") {
163            Ok(KrakenError::PermissionDenied)
164        } else if value.starts_with("EAPI:Invalid key") {
165            Ok(KrakenError::InvalidKey)
166        } else if value.starts_with("EQuery:Unknown asset pair") {
167            Ok(KrakenError::UnknownAssetPair)
168        } else if value.starts_with("EGeneral:Invalid arguments") {
169            Ok(KrakenError::InvalidArguments(value.clone()))
170        } else if value.starts_with("EAPI:Invalid signature") {
171            Ok(KrakenError::InvalidSignature)
172        } else if value.starts_with("EAPI:Invalid nonce") {
173            Ok(KrakenError::InvalidNonce)
174        } else if value.starts_with("ESession:Invalid session") {
175            Ok(KrakenError::InvalidSession)
176        } else if value.starts_with("EAPI:Bad request") {
177            Ok(KrakenError::BadRequest)
178        } else if value.starts_with("EGeneral:Unknown Method") {
179            Ok(KrakenError::UnknownMethod)
180        } else if value.starts_with("EAPI:Rate limit exceeded") {
181            Ok(KrakenError::RateLimitExceeded)
182        } else if value.starts_with("EOrder:Rate limit exceeded") {
183            Ok(KrakenError::TradingRateLimitExceeded)
184        } else if value.starts_with("EGeneral:Temporary lockout") {
185            Ok(KrakenError::TemporaryLockout)
186        } else if value.starts_with("EService:Unavailable") {
187            Ok(KrakenError::ServiceUnavailable)
188        } else if value.starts_with("EService:Busy") {
189            Ok(KrakenError::ServiceBusy)
190        } else if value.starts_with("EGeneral:Internal error") {
191            Ok(KrakenError::InternalError)
192        } else if value.starts_with("ETrade:Locked") {
193            Ok(KrakenError::TradeLocked)
194        } else if value.starts_with("EAPI:Feature disabled") {
195            Ok(KrakenError::FeatureDisabled)
196        } else {
197            Err(())
198        }
199    }
200}