This is a simple implementation of Ledger with Rust.
Educational purposes only.
First project in Rust.
- The code supports handling deposits, withdrawals, disputes, resolutions and chargebacks.
- It reads transactions from a CSV file and processes them accordingly.
- It maintains client accounts with available, held, and total balances.
- It includes error handling for various scenarios, such as invalid transactions and insufficient funds
- It outputs the final state of all client accounts to stdout in CSV format.
- Structures are validated from loaded CSV data.
- Streams the input
cargo build # build
cargo test # run tests
cargo run # run
cargo run -- transactions.csv > accounts.csv
The input CSV file should have the following columns:
- type: The type of transaction (
deposit,withdrawal,dispute,resolve,chargeback). Format: string. - client: The client ID. Format: number.
- tx: Transaction ID. Format: number.
- amount: Value of transaction. Format: decimal number with precision 4 (max supported).
CSV will look like this:
- client: The ID of the client from the input. Format: number
- available: The available balance for the client. Format: decimal number with precision 4
- held: The held balance for the client. Format: decimal number with precision 4
- total: The total balance for the client. Format: decimal number with precision 4
- locked: Whether the account is locked. Format: boolean
- cargo
- RustRover
- Claude Code - Review the code in the end
- ChatGPT to generate input and output data for tests
- ChatGPT to research Rust libraries and syntax (this is first ever Rust project)
- NO Copilot or AI Agents, we are learning Rust ourselves :)
- Rust unit tests are supposed to be included on bottom of the file (units) - that feels a bit strange, but has it's good sides as well
- Whoever reads this, code style is as you may think for first time user of Rust, messy
- audit log for disputes is not implemented, I ommited this on purpose (at least for now)
- addition and substraction are interchangable in order, but the whole code assumes the chronological order in input file
- for simplicity, output will always have 4 decimals
- if dispute is made for larger amount than available, the available will go negative (sometimes happens when someone is scamed for example)
- Chargeback freezes account, but should also create withdrawal transaction for held amount. This will be not implemented, as it could create clashes between transaction ids, and those are incremental probably for now.
- Deposit, Withdrawal, Dispute, Chargeback of Deposit use case is not supported (as this should result in 0 total amount, with internal movement from Treasury to cover negative balance etc), this is out of scope for this project. But totally valid case in production environment.
- Let's assume the chargebacks lock the client, which will not really happen in real life, because client can have different funds, not only the ones being disputed.
- I could use some lib for CLI argument parsing, but for simplicity I just used std::env::args
- This is educational project, not for production use!
- No integrations test, probably should be added later
- Enums, and serde used for CSV parsing
- Money stored as i64 internally, with defined max precision
- It was fun, but will no go deeper of this rabbit hole for now :)