Dealing with Banking Data in Rust: a Proof of Concept
This crate is a simple proof of concept for updating banking data in Rust, focusing on the core logic.
This is a proof of concept only, not to use with real data. This crate does not implement any encryption nor other data protection mechanism, and is thus not suitable for dealing with private or otherwise secret data.
How to use
Requirements
Building this crate requires:
- A Rust 2021 compiler, and optionally
cargo
. Instructions for how to install the latest stable Rust toolchain can be found on the rust-lang website. - The
itertools
(version 0.10) andatty
(version 0.2) crates (they may be downloaded by cargo at compile time).
The compiled executable can be run on any terminal or terminal emulator.
Build
The canonical way to build the executable is to run the command
cargo build --release
from the crate main directory.
The Makefile provides six additional commands (require make
):
make run
: build the crate in offline mode, run the executable with on filetransactions.csv
, and save the output inaccounts.csv
make build
: build the crate in offline modemake build_no_color
: build the crate without color formatting for the warningstest
: build and run the unit testsclippy
: build the crate and runclippy
clean
: delete thetarget
folder andCargo.lock
file
Run
The executable takes the name of a file with transactions as parameter (see below). For instance, to analyse transactions in the file transactions.csv
, run
./target/release/banking_exercise transactions.csv
Alternatively, the command
cargo run --release -- filename
builds and runs the executable.
By default, results are printed to stdout
and warnings to stderr
. They can be redirected to files output_file
and error_file
by appending > output_file
and 2> error_file
to the command. For instance,
./target/release/banking_exercise transactions.csv > client_data.csv
will save the results in the file client_data.csv
, and
./target/release/banking_exercise transactions.csv > client_data.csv 2> log.txt
will additionally save the errors to log.txt
.
How does it work?
High-level
The executable goes through the lines of the file passed as parameter, one by one. It tries to parse each line as a valid transaction and, if successful, updates the data accordingly. If the client ID does not exist, a new Client
instance is created before performing the transaction, with 0.
available and held funds, an unlocked account, and an empty transaction history. After the last line has been analysed, the data is printed to stdout
.
No transaction can be performed on a locked account.
Warnings are printed to stderr
if a row can not be parsed as a valid transaction or contains more fields than expected. By default, these warnings are printed in bold red. This behaviour can be overridden by building with the no_color
feature, by compiling with the --no-default-features
flag, or by redirecting stderr
to a file, in which case warnings are printed using the default terminal colour and font family.
Warning: No warning is printed if the first line does not represent a valid transaction.
Transaction file format
The input file should contain transactions separated by newlines, with the fields in each transaction separated by commas. Possible transactions are: deposit
, withdrawal
, dispute
, resolve
, and chargeback
, with the following fields:
deposit
orwithdrawal
:transaction_id
(ID of the current transaction),client_id
(ID of the client), andamount
(amount deposited or withdrawn);dispute
,resolve
, orchargeback
:transaction_id
(ID of the transaction which is disputed, resolved, or charged back) andclient_id
(ID of the client).
Client data
For each client, we show an ID (u16
), amounts of available, held, and total funds (f64
), and whether the account is locked (bool
).
Some implementation details
The crate defines the following structures:
Transaction
: anenum
type of the formDeposit(amount)
,Withdrawal(amount)
,Dispute(transaction_id)
,Resolve(transaction_id)
, orChargeback(transaction_id)
Client
: a structure storing the client's ID, the available and held amounts in their account, a boolean value indicating whether the account is locked, a transaction history (implemented as a hashmap with transaction IDs as keys and transactions as values), and a list of disputed transactions (implemented as a set of transaction IDs)ClientMap
: aHashMap
with client IDs as keys andClient
s as valuesTransactionID
: a transaction ID (wrapper around au32
)ClientID
: a client ID (wrapper around au16
)
The latter two structures are primaruly there to help ensure correctness, avoiding a client ID to be mistaken for a transaction ID or conversely.
The total amount in a client's account is not stored explicitly, but computed as the sum of the available and held amounts when needed.
Transactions without an explicit ID (Dispute
, Resolve
, and Chargeback
) are assigned the ID 0
. They are not included in the client's trasaction history.
Assumptions
- Each client has a unique ID.
- Two different
Deposit
orWithdrawal
transactions for the same client have different transaction IDs. - No explicit transaction ID is 0.