tinkoff_bank/
data_structs.rs

1use chrono::{serde::ts_milliseconds, DateTime, Utc};
2use serde::{Deserialize, Deserializer};
3
4#[derive(Deserialize, Debug, PartialEq)]
5pub enum AccessLevel {
6    #[serde(rename = "ANONYMOUS")]
7    Anonymous,
8    #[serde(rename = "CANDIDATE")]
9    Candidate,
10    #[serde(rename = "CLIENT")]
11    Client,
12}
13
14#[derive(Deserialize, Debug, PartialEq)]
15pub struct UserInfo {
16    #[serde(rename = "accessLevel")]
17    pub access_level: AccessLevel,
18    #[serde(default, rename = "userId")]
19    pub user_id: String,
20}
21
22#[derive(Deserialize, Debug, PartialEq)]
23pub struct Session {
24    #[serde(rename = "sessionid")]
25    pub id: String,
26    pub ttl: u32,
27}
28
29#[derive(Deserialize, Debug, PartialEq)]
30pub enum ResultCode {
31    #[serde(rename = "OK")]
32    Ok,
33    #[serde(rename = "WAITING_CONFIRMATION")]
34    WaitingConfirmation,
35}
36
37#[derive(Deserialize, Debug, PartialEq)]
38pub struct Nothing {}
39
40#[derive(Deserialize, Debug, PartialEq)]
41pub struct Account {
42    #[serde(rename = "externalAccountNumber")]
43    pub external_number: String,
44    #[serde(rename = "accountGroup")]
45    pub group: String,
46    #[serde(rename = "moneyAmount")]
47    pub money_amount: MoneyAmount,
48    pub name: String,
49    pub id: String,
50}
51
52#[derive(Deserialize, Debug, PartialEq)]
53pub struct MoneyAmount {
54    pub currency: Currency,
55    pub value: f32,
56}
57
58#[allow(clippy::upper_case_acronyms)]
59#[derive(Debug, PartialEq)]
60pub enum Currency {
61    EUR,
62    RUB,
63    USD,
64    BYN,
65}
66
67impl<'de> Deserialize<'de> for Currency {
68    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
69    where
70        D: Deserializer<'de>,
71    {
72        #[derive(Deserialize)]
73        struct Outer {
74            name: Inner,
75        }
76
77        #[allow(clippy::upper_case_acronyms)]
78        #[derive(Deserialize)]
79        enum Inner {
80            RUB,
81            USD,
82            EUR,
83            BYN,
84        }
85
86        let helper = Outer::deserialize(deserializer)?;
87        Ok(match helper.name {
88            Inner::RUB => Currency::RUB,
89            Inner::USD => Currency::USD,
90            Inner::EUR => Currency::EUR,
91            Inner::BYN => Currency::BYN,
92        })
93    }
94}
95
96#[derive(Deserialize, Debug, PartialEq)]
97pub struct ResponsePayload<T> {
98    #[serde(rename = "resultCode")]
99    pub result_code: ResultCode,
100    // exists for success response
101    pub payload: Option<T>,
102    // exists if confirmation required
103    pub confirmations: Option<Vec<String>>,
104    #[serde(rename = "initialOperation")]
105    pub initial_operation: Option<String>,
106    #[serde(rename = "operationTicket")]
107    pub operation_ticket: Option<String>,
108}
109
110#[derive(Deserialize, Debug, PartialEq)]
111pub enum OperationType {
112    Credit,
113    Debit,
114}
115
116#[derive(Deserialize, Debug, PartialEq)]
117pub enum OperationGroup {
118    #[serde(rename = "PAY")]
119    Pay,
120    #[serde(rename = "INCOME")]
121    Income,
122    #[serde(rename = "TRANSFER")]
123    Transfer,
124    #[serde(rename = "CASH")]
125    Cash,
126    #[serde(rename = "CORRECTION")]
127    Correction,
128    #[serde(rename = "CHARGE")]
129    Charge,
130    #[serde(rename = "INTERNAL")]
131    Internal,
132}
133
134#[derive(Debug, PartialEq)]
135pub struct Operation {
136    pub id: String,
137    pub operation_type: OperationType,
138    pub description: String,
139    pub amount: MoneyAmount,
140    pub account_amount: MoneyAmount,
141    pub operation_time: DateTime<Utc>,
142    pub spending_category: String,
143    pub mcc: u16,
144    pub category: String,
145    pub subcategory: Option<String>,
146    pub account: String,
147    pub merchant: Option<String>,
148    pub group: OperationGroup,
149    pub subgroup: Option<String>,
150}
151
152impl<'de> Deserialize<'de> for Operation {
153    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
154    where
155        D: Deserializer<'de>,
156    {
157        #[derive(Deserialize)]
158        struct Outer {
159            id: String,
160            #[serde(rename = "type")]
161            operation_type: OperationType,
162            description: String,
163            amount: MoneyAmount,
164            #[serde(rename = "accountAmount")]
165            account_amount: MoneyAmount,
166            #[serde(rename = "operationTime")]
167            operation_time: InnerTime,
168            #[serde(rename = "spendingCategory")]
169            spending_category: InnerName,
170            mcc: u16,
171            category: InnerName,
172            subcategory: Option<String>,
173            account: String,
174            merchant: Option<InnerName>,
175            group: OperationGroup,
176            subgroup: Option<InnerName>,
177        }
178
179        #[derive(Deserialize)]
180        struct InnerName {
181            name: String,
182        }
183
184        #[derive(Deserialize)]
185        struct InnerTime {
186            #[serde(with = "ts_milliseconds")]
187            milliseconds: DateTime<Utc>,
188        }
189
190        let helper = Outer::deserialize(deserializer)?;
191        Ok(Operation {
192            id: helper.id,
193            operation_type: helper.operation_type,
194            description: helper.description,
195            amount: helper.amount,
196            account_amount: helper.account_amount,
197            operation_time: helper.operation_time.milliseconds,
198            spending_category: helper.spending_category.name,
199            mcc: helper.mcc,
200            category: helper.category.name,
201            subcategory: helper.subcategory,
202            account: helper.account,
203            merchant: match helper.merchant {
204                Some(val) => Some(val.name),
205                None => None,
206            },
207            group: helper.group,
208            subgroup: match helper.subgroup {
209                Some(val) => Some(val.name),
210                None => None,
211            },
212        })
213    }
214}