xtb_client/schema/
api_errors.rs

1use std::fmt;
2use std::str::FromStr;
3
4use serde_with::DeserializeFromStr;
5use thiserror::Error;
6
7/// Rust enum definition for error codes
8#[derive(Debug, Clone, PartialEq, DeserializeFromStr, Default)]
9pub enum XtbErrorCode {
10    /// Invalid price
11    #[default]
12    BE001,
13    /// Invalid StopLoss or TakeProfit
14    BE002,
15    /// Invalid volume
16    BE003,
17    /// Login disabled
18    BE004,
19    /// userPasswordCheck: Invalid login or password.
20    BE005,
21    /// Market for instrument is closed
22    BE006,
23    /// Mismatched parameters
24    BE007,
25    /// Modification is denied
26    BE008,
27    /// Not enough money on account to perform trade
28    BE009,
29    /// Off quotes
30    BE010,
31    /// Opposite positions prohibited
32    BE011,
33    /// Short positions prohibited
34    BE012,
35    /// Price has changed
36    BE013,
37    /// Request too frequent
38    BE014,
39    /// Too many trade requests
40    BE016,
41    /// Too many trade requests
42    BE017,
43    /// Trading on instrument disabled
44    BE018,
45    /// Trading timeout
46    BE019,
47    /// Symbol does not exist for given account
48    BE094,
49    /// Account cannot trade on given symbol
50    BE095,
51    /// Pending order cannot be closed. Pending order must be deleted
52    BE096,
53    /// Cannot close already closed order
54    BE097,
55    /// No such transaction
56    BE098,
57    /// Unknown instrument symbol
58    BE101,
59    /// Unknown transaction type
60    BE102,
61    /// User is not logged
62    BE103,
63    /// Method does not exist
64    BE104,
65    /// Incorrect period given
66    BE105,
67    /// Missing data
68    BE106,
69    /// Incorrect command format
70    BE110,
71    /// Symbol does not exist
72    BE115,
73    /// Symbol does not exist
74    BE116,
75    /// Invalid token
76    BE117,
77    /// User already logged
78    BE118,
79    /// Session timed out.
80    BE200,
81    /// Invalid parameters
82    EX000,
83    /// Internal error, in case of such error, please contact support
84    EX001,
85    /// Internal error, in case of such error, please contact support
86    EX002,
87    /// Internal error, in case of such error, please contact support
88    BE000,
89    /// Internal error, request timed out
90    EX003,
91    /// Login credentials are incorrect or this login is not allowed to use an application with this appId
92    EX004,
93    /// Internal error, system overloaded
94    EX005,
95    /// No access
96    EX006,
97    /// userPasswordCheck: Invalid login or password. This login/password is disabled for 10 minutes (the specific login and password pair is blocked after an unsuccessful login attempt).
98    EX007,
99    /// You have reached the connection limit. For details see the Connection validation section.
100    EX008,
101    /// Data limit potentially exceeded. Please narrow your request range. The potential data size is calculated by: (end_time - start_time) / interval. The limit is 50 000 candles
102    EX009,
103    /// Your login is on the black list, perhaps due to previous misuse. For details please contact support.
104    EX010,
105    /// You are not allowed to execute this command. For details please contact support.
106    EX011,
107    /// BE20-37 and BE99 - Other error
108    OtherError(u8),
109    /// SExxx - Internal server error
110    InternalServerError(u16)
111}
112
113impl FromStr for XtbErrorCode {
114    type Err = XtbErrorCodeError;
115
116    fn from_str(s: &str) -> Result<XtbErrorCode, Self::Err> {
117        match s {
118            "BE001" => Ok(XtbErrorCode::BE001),
119            "BE002" => Ok(XtbErrorCode::BE002),
120            "BE003" => Ok(XtbErrorCode::BE003),
121            "BE004" => Ok(XtbErrorCode::BE004),
122            "BE005" => Ok(XtbErrorCode::BE005),
123            "BE006" => Ok(XtbErrorCode::BE006),
124            "BE007" => Ok(XtbErrorCode::BE007),
125            "BE008" => Ok(XtbErrorCode::BE008),
126            "BE009" => Ok(XtbErrorCode::BE009),
127            "BE010" => Ok(XtbErrorCode::BE010),
128            "BE011" => Ok(XtbErrorCode::BE011),
129            "BE012" => Ok(XtbErrorCode::BE012),
130            "BE013" => Ok(XtbErrorCode::BE013),
131            "BE014" => Ok(XtbErrorCode::BE014),
132            "BE016" => Ok(XtbErrorCode::BE016),
133            "BE017" => Ok(XtbErrorCode::BE017),
134            "BE018" => Ok(XtbErrorCode::BE018),
135            "BE019" => Ok(XtbErrorCode::BE019),
136            "BE094" => Ok(XtbErrorCode::BE094),
137            "BE095" => Ok(XtbErrorCode::BE095),
138            "BE096" => Ok(XtbErrorCode::BE096),
139            "BE097" => Ok(XtbErrorCode::BE097),
140            "BE098" => Ok(XtbErrorCode::BE098),
141            "BE101" => Ok(XtbErrorCode::BE101),
142            "BE102" => Ok(XtbErrorCode::BE102),
143            "BE103" => Ok(XtbErrorCode::BE103),
144            "BE104" => Ok(XtbErrorCode::BE104),
145            "BE105" => Ok(XtbErrorCode::BE105),
146            "BE106" => Ok(XtbErrorCode::BE106),
147            "BE110" => Ok(XtbErrorCode::BE110),
148            "BE115" => Ok(XtbErrorCode::BE115),
149            "BE116" => Ok(XtbErrorCode::BE116),
150            "BE117" => Ok(XtbErrorCode::BE117),
151            "BE118" => Ok(XtbErrorCode::BE118),
152            "BE200" => Ok(XtbErrorCode::BE200),
153            "EX000" => Ok(XtbErrorCode::EX000),
154            "EX001" => Ok(XtbErrorCode::EX001),
155            "EX002" => Ok(XtbErrorCode::EX002),
156            "BE000" => Ok(XtbErrorCode::BE000),
157            "EX003" => Ok(XtbErrorCode::EX003),
158            "EX004" => Ok(XtbErrorCode::EX004),
159            "EX005" => Ok(XtbErrorCode::EX005),
160            "EX006" => Ok(XtbErrorCode::EX006),
161            "EX007" => Ok(XtbErrorCode::EX007),
162            "EX008" => Ok(XtbErrorCode::EX008),
163            "EX009" => Ok(XtbErrorCode::EX009),
164            "EX010" => Ok(XtbErrorCode::EX010),
165            "EX011" => Ok(XtbErrorCode::EX011),
166            v @ ("BE020" | "BE021" | "BE022" | "BE023" | "BE024" | "BE025" | "BE026" | "BE027" | "BE028" | "BE029" | "BE030" | "BE031" | "BE032" | "BE033" | "BE034" | "BE035" | "BE036" | "BE037" | "BE099") => parse_other_error(v),
167            v if v.starts_with("SE") => parse_se_error(v),
168            v => Err(XtbErrorCodeError::UnsupportedErrorCode(v.to_owned())),
169        }
170    }
171}
172
173
174#[derive(Debug, PartialEq, Error)]
175pub enum XtbErrorCodeError {
176    #[error("The error code '{0}' is not supported by the 'xtb_client' library")]
177    UnsupportedErrorCode(String),
178}
179
180
181
182fn parse_other_error(err_str: &str) -> Result<XtbErrorCode, XtbErrorCodeError> {
183    if !err_str.starts_with("BE0") || err_str.len() != 5 {
184        Err(XtbErrorCodeError::UnsupportedErrorCode(err_str.to_owned()))
185    } else {
186        let digits = &err_str[3..];
187        let c = u8::from_str(digits).map_err(|_| XtbErrorCodeError::UnsupportedErrorCode(err_str.to_owned()))?;
188        Ok(XtbErrorCode::OtherError(c))
189    }
190}
191
192
193fn parse_se_error(err_str: &str) -> Result<XtbErrorCode, XtbErrorCodeError> {
194    if !err_str.starts_with("SE") || err_str.len() != 5 {
195        Err(XtbErrorCodeError::UnsupportedErrorCode(err_str.to_owned()))
196    } else {
197        let digits = &err_str[2..];
198        let c = u16::from_str(digits).map_err(|_| XtbErrorCodeError::UnsupportedErrorCode(err_str.to_owned()))?;
199        Ok(XtbErrorCode::InternalServerError(c))
200    }
201}
202
203
204impl fmt::Display for XtbErrorCode {
205    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206        match *self {
207            XtbErrorCode::BE001 => f.write_str("BE001"),
208            XtbErrorCode::BE002 => f.write_str("BE002"),
209            XtbErrorCode::BE003 => f.write_str("BE003"),
210            XtbErrorCode::BE004 => f.write_str("BE004"),
211            XtbErrorCode::BE005 => f.write_str("BE005"),
212            XtbErrorCode::BE006 => f.write_str("BE006"),
213            XtbErrorCode::BE007 => f.write_str("BE007"),
214            XtbErrorCode::BE008 => f.write_str("BE008"),
215            XtbErrorCode::BE009 => f.write_str("BE009"),
216            XtbErrorCode::BE010 => f.write_str("BE010"),
217            XtbErrorCode::BE011 => f.write_str("BE011"),
218            XtbErrorCode::BE012 => f.write_str("BE012"),
219            XtbErrorCode::BE013 => f.write_str("BE013"),
220            XtbErrorCode::BE014 => f.write_str("BE014"),
221            XtbErrorCode::BE016 => f.write_str("BE016"),
222            XtbErrorCode::BE017 => f.write_str("BE017"),
223            XtbErrorCode::BE018 => f.write_str("BE018"),
224            XtbErrorCode::BE019 => f.write_str("BE019"),
225            XtbErrorCode::BE094 => f.write_str("BE094"),
226            XtbErrorCode::BE095 => f.write_str("BE095"),
227            XtbErrorCode::BE096 => f.write_str("BE096"),
228            XtbErrorCode::BE097 => f.write_str("BE097"),
229            XtbErrorCode::BE098 => f.write_str("BE098"),
230            XtbErrorCode::BE101 => f.write_str("BE101"),
231            XtbErrorCode::BE102 => f.write_str("BE102"),
232            XtbErrorCode::BE103 => f.write_str("BE103"),
233            XtbErrorCode::BE104 => f.write_str("BE104"),
234            XtbErrorCode::BE105 => f.write_str("BE105"),
235            XtbErrorCode::BE106 => f.write_str("BE106"),
236            XtbErrorCode::BE110 => f.write_str("BE110"),
237            XtbErrorCode::BE115 => f.write_str("BE115"),
238            XtbErrorCode::BE116 => f.write_str("BE116"),
239            XtbErrorCode::BE117 => f.write_str("BE117"),
240            XtbErrorCode::BE118 => f.write_str("BE118"),
241            XtbErrorCode::BE200 => f.write_str("BE200"),
242            XtbErrorCode::EX000 => f.write_str("EX000"),
243            XtbErrorCode::EX001 => f.write_str("EX001"),
244            XtbErrorCode::EX002 => f.write_str("EX002"),
245            XtbErrorCode::BE000 => f.write_str("BE000"),
246            XtbErrorCode::EX003 => f.write_str("EX003"),
247            XtbErrorCode::EX004 => f.write_str("EX004"),
248            XtbErrorCode::EX005 => f.write_str("EX005"),
249            XtbErrorCode::EX006 => f.write_str("EX006"),
250            XtbErrorCode::EX007 => f.write_str("EX007"),
251            XtbErrorCode::EX008 => f.write_str("EX008"),
252            XtbErrorCode::EX009 => f.write_str("EX009"),
253            XtbErrorCode::EX010 => f.write_str("EX010"),
254            XtbErrorCode::EX011 => f.write_str("EX011"),
255            XtbErrorCode::OtherError(c) => f.write_str(&format!("BE{:03}", c)),
256            XtbErrorCode::InternalServerError(c) => f.write_str(&format!("SE{:03}", c)),
257        }
258    }
259}
260
261
262#[cfg(test)]
263mod tests {
264    mod other_error_parser {
265        use rstest::rstest;
266
267        use crate::schema::api_errors::{parse_other_error, XtbErrorCode, XtbErrorCodeError};
268
269        #[rstest]
270        #[case("BE020", 20)]
271        #[case("BE021", 21)]
272        #[case("BE022", 22)]
273        #[case("BE023", 23)]
274        #[case("BE024", 24)]
275        #[case("BE025", 25)]
276        #[case("BE026", 26)]
277        #[case("BE027", 27)]
278        #[case("BE028", 28)]
279        #[case("BE029", 29)]
280        #[case("BE030", 30)]
281        #[case("BE031", 31)]
282        #[case("BE032", 32)]
283        #[case("BE033", 33)]
284        #[case("BE034", 34)]
285        #[case("BE035", 35)]
286        #[case("BE036", 36)]
287        #[case("BE037", 37)]
288        #[case("BE099", 99)]
289        fn parse_valid_value(#[case] input: &str, #[case] expected_code: u8) {
290            assert_eq!(parse_other_error(input), Ok(XtbErrorCode::OtherError(expected_code)));
291        }
292
293        #[rstest]
294        #[case("BE100")]
295        #[case("BE120")]
296        #[case("BEA20")]
297        #[case("BE030A")]
298        #[case("BE0300")]
299        fn parse_invalid_value(#[case] input: &str) {
300            assert_eq!(parse_other_error(input), Err(XtbErrorCodeError::UnsupportedErrorCode(input.to_owned())));
301        }
302    }
303
304    mod se_error_parser {
305        use rstest::rstest;
306
307        use crate::schema::api_errors::{parse_se_error, XtbErrorCode, XtbErrorCodeError};
308
309        #[rstest]
310        #[case("SE000", 0)]
311        #[case("SE099", 99)]
312        #[case("SE100", 100)]
313        #[case("SE555", 555)]
314        #[case("SE999", 999)]
315        fn parse_valid_value(#[case] input: &str, #[case] expected_code: u16) {
316            assert_eq!(parse_se_error(input), Ok(XtbErrorCode::InternalServerError(expected_code)));
317        }
318
319        #[rstest]
320        #[case("SEE000")]
321        #[case("SEABC")]
322        #[case("SE0000")]
323        fn parse_invalid_value(#[case] input: &str) {
324            assert_eq!(parse_se_error(input), Err(XtbErrorCodeError::UnsupportedErrorCode(input.to_owned())));
325        }
326    }
327
328    mod xtb_error_operations {
329        use std::str::FromStr;
330
331        use rstest::rstest;
332        use rstest_reuse::{self, *};
333        use serde_json::from_str;
334
335        use crate::schema::api_errors::XtbErrorCode;
336
337        #[template]
338        #[rstest]
339        #[case("BE001", XtbErrorCode::BE001)]
340        #[case("BE002", XtbErrorCode::BE002)]
341        #[case("BE003", XtbErrorCode::BE003)]
342        #[case("BE004", XtbErrorCode::BE004)]
343        #[case("BE005", XtbErrorCode::BE005)]
344        #[case("BE006", XtbErrorCode::BE006)]
345        #[case("BE007", XtbErrorCode::BE007)]
346        #[case("BE008", XtbErrorCode::BE008)]
347        #[case("BE009", XtbErrorCode::BE009)]
348        #[case("BE010", XtbErrorCode::BE010)]
349        #[case("BE011", XtbErrorCode::BE011)]
350        #[case("BE012", XtbErrorCode::BE012)]
351        #[case("BE013", XtbErrorCode::BE013)]
352        #[case("BE014", XtbErrorCode::BE014)]
353        #[case("BE016", XtbErrorCode::BE016)]
354        #[case("BE017", XtbErrorCode::BE017)]
355        #[case("BE018", XtbErrorCode::BE018)]
356        #[case("BE019", XtbErrorCode::BE019)]
357        #[case("BE094", XtbErrorCode::BE094)]
358        #[case("BE095", XtbErrorCode::BE095)]
359        #[case("BE096", XtbErrorCode::BE096)]
360        #[case("BE097", XtbErrorCode::BE097)]
361        #[case("BE098", XtbErrorCode::BE098)]
362        #[case("BE101", XtbErrorCode::BE101)]
363        #[case("BE102", XtbErrorCode::BE102)]
364        #[case("BE103", XtbErrorCode::BE103)]
365        #[case("BE104", XtbErrorCode::BE104)]
366        #[case("BE105", XtbErrorCode::BE105)]
367        #[case("BE106", XtbErrorCode::BE106)]
368        #[case("BE110", XtbErrorCode::BE110)]
369        #[case("BE115", XtbErrorCode::BE115)]
370        #[case("BE116", XtbErrorCode::BE116)]
371        #[case("BE117", XtbErrorCode::BE117)]
372        #[case("BE118", XtbErrorCode::BE118)]
373        #[case("BE200", XtbErrorCode::BE200)]
374        #[case("EX000", XtbErrorCode::EX000)]
375        #[case("EX001", XtbErrorCode::EX001)]
376        #[case("EX002", XtbErrorCode::EX002)]
377        #[case("BE000", XtbErrorCode::BE000)]
378        #[case("EX003", XtbErrorCode::EX003)]
379        #[case("EX004", XtbErrorCode::EX004)]
380        #[case("EX005", XtbErrorCode::EX005)]
381        #[case("EX006", XtbErrorCode::EX006)]
382        #[case("EX007", XtbErrorCode::EX007)]
383        #[case("EX008", XtbErrorCode::EX008)]
384        #[case("EX009", XtbErrorCode::EX009)]
385        #[case("EX010", XtbErrorCode::EX010)]
386        #[case("EX011", XtbErrorCode::EX011)]
387
388        #[case("BE020", XtbErrorCode::OtherError(20u8))]
389        #[case("BE021", XtbErrorCode::OtherError(21u8))]
390        #[case("BE022", XtbErrorCode::OtherError(22u8))]
391        #[case("BE023", XtbErrorCode::OtherError(23u8))]
392        #[case("BE024", XtbErrorCode::OtherError(24u8))]
393        #[case("BE025", XtbErrorCode::OtherError(25u8))]
394        #[case("BE026", XtbErrorCode::OtherError(26u8))]
395        #[case("BE027", XtbErrorCode::OtherError(27u8))]
396        #[case("BE028", XtbErrorCode::OtherError(28u8))]
397        #[case("BE029", XtbErrorCode::OtherError(29u8))]
398        #[case("BE030", XtbErrorCode::OtherError(30u8))]
399        #[case("BE031", XtbErrorCode::OtherError(31u8))]
400        #[case("BE032", XtbErrorCode::OtherError(32u8))]
401        #[case("BE033", XtbErrorCode::OtherError(33u8))]
402        #[case("BE034", XtbErrorCode::OtherError(34u8))]
403        #[case("BE035", XtbErrorCode::OtherError(35u8))]
404        #[case("BE036", XtbErrorCode::OtherError(36u8))]
405        #[case("BE037", XtbErrorCode::OtherError(37u8))]
406        #[case("BE099", XtbErrorCode::OtherError(99u8))]
407
408        #[case("SE000", XtbErrorCode::InternalServerError(0u16))]
409        #[case("SE099", XtbErrorCode::InternalServerError(99u16))]
410        #[case("SE100", XtbErrorCode::InternalServerError(100u16))]
411        #[case("SE555", XtbErrorCode::InternalServerError(555u16))]
412        #[case("SE999", XtbErrorCode::InternalServerError(999u16))]
413        fn variant_test_template(#[case] input: &str, #[case] variant: XtbErrorCode) {}
414
415        #[apply(variant_test_template)]
416        fn parse_from_str(#[case] input: &str, #[case] variant: XtbErrorCode) {
417            assert_eq!(XtbErrorCode::from_str(input), Ok(variant))
418        }
419
420        #[apply(variant_test_template)]
421        fn convert_to_str(#[case] input: &str, #[case] variant: XtbErrorCode) {
422            assert_eq!(format!("{}", variant), input.to_owned())
423        }
424
425        #[apply(variant_test_template)]
426        fn deserialize(#[case] input: &str, #[case] variant: XtbErrorCode) {
427            let deserialized: XtbErrorCode = from_str(&format!("\"{}\"", input)).unwrap();
428            assert_eq!(deserialized, variant)
429        }
430    }
431}