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
use crate::api::Mode;
use chrono::prelude::*;
/// In general, an error will be returned in the following format.
/// Lemons docks: [Error Handling](https://docs.lemon.markets/error-handling)
use reqwest::{Error as ReqwestError, StatusCode};
use serde::{Deserialize, Serialize};
use serde_json::Error as JsonError;
use thiserror::Error;

/// Error type for the library
#[derive(Debug, Error)]
pub enum Error {
    #[error("Encountered an Reqwest related error")]
    /// Error type for Reqwest errors
    Reqwest(
        #[from]
        #[source]
        ReqwestError,
    ),

    /// Error type for Json errors
    #[error("Encountered an Json related error")]
    Json(#[from] JsonError),

    /// Error type for StatusCode errors
    #[error("HTTP Error {0}")]
    Http(StatusCode),

    /// Error type for other errors
    #[error("{0}")]
    Str(String),
}

/// Error type for the Lemon API
#[derive(Deserialize)]
pub struct LemonError {
    /// The time that the error occurred
    time: DateTime<Utc>,
    /// API mode.
    mode: Mode,
    /// Status Code of the error
    status: String,
    /// Lemon API error code
    error_code: ErrorCode,
    /// Error message
    error_message: String,
}

/// Error codes for the Lemon API
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ErrorCode {
    /// The API key is not provided in the HTTP header,
    /// cannot be decoded by the backend,
    /// or the API Key does not exist.
    Unauthorized,
    /// The API key is revoked or user is deleted/suspended.
    TokenInvalid,
    /// The API key has exceeded its rate limit.
    /// Please respect the value of the Retry-After
    /// header before performing another request.
    RateLimitExceeded,
    // TODO: Add [xxx_not_found]
    /// An error occurred in the backend.
    /// This is not your fault.
    /// We will investigate this.
    InternalError,
    /// Same idempotency has been used within current 7 day period.
    OrderIdempotencyViolation,
    /// Cannot withdraw money because the PIN is not provided in the request (money only).
    PinMissing,
    /// Cannot withdraw money because the PIN is not set (money only)
    PinNotSet,
    /// Cannot withdraw money because the pin verification failed
    PinInvalid,
    /// Cannot withdraw money because there are insufficient funds on the account
    WithdrawInsufficientFunds,
    /// Cannot withdraw money because the daily payout limit is exceeded
    WithdrawLimitExceeded,
    /// Cannot withdraw money because the maximal daily request limit is exceeded
    WithdrawRequestLimitExceeded,
    /// Cannot update address_country when trading is enabled or user is onboarded
    ForbiddenInCurrentState,
    /// Cannot update trading plan to basic/pro
    PlanNotAllowed,
    ///    insufficient instrument holdings [on order sell]
    InsufficientHoldings,
    ///    cannot place order if one expires before market opens again
    OrderExpirationDateInvalid,
    ///    cannot place/activate buy order if estimated total price is greater than 25k Euro
    OrderTotalPriceLimitExceeded,
    ///cannot place order in ALLDAY for MONEY env
    ForbiddenForVenue,
    ///    cannot place order if trading is not enabled
    TradingDisabled,
    ///maximum daily amount of orders reached
    OrderLimitExceeded,
    ///failed to place order if instrument is not tradable
    InstrumentNotTradable,
    ///cannot place/activate buy order because of insufficient account funds
    AccountInsufficientFunds,
    ///cannot place/activate order because trading is blocked globally
    TradingBlocked,
    ///cannot activate order if its status != inactive
    OrderNotInactive,
    /// cannot delete order if its not in cancelling/cancelled/expired/executed/rejected state
    OrderNotTerminated,
}

impl std::fmt::Display for ErrorCode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl std::fmt::Display for LemonError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "time: {}, mode: {}, status: {}, error_code: {}, error_message: {}",
            self.time, self.mode, self.status, self.error_code, self.error_message
        )
    }
}