This crate tries to model the very basics of the bookkeeping activity. it is a new rustacean's first open source crate.
Outline
A book contains
- accounts,
- units (may represent currencies)
- and transactions, which in turn, contain moves.
Features
- Book balance guaranteed at compile time.
- Arbitrary metadata may be stored in books, accounts, units, transactions and moves.
Defficiencies
- No optimization of balance calculations.
Tutorial
Moving money around and getting balances
// Yo, welcome to the `bookkeeping` crate.
use *;
// You may be wondering why a crate tutorial starts with "Yo".
// Well, there's no good explanation for that. It is what it is.
// This crate aims to provide a neat API and amusing documentation.
// If you bear it through the tutorial, you should be able to start
// bookkeeping (that's a noun) in no-time.
//
// In case you didn't know, bookkeeping is about keeping record of money
// moving around. So, our goal in this tutorial is to teach you how to
// keep records of money moving around using this crate.
// The first thing you should know is that all records are stored in
// books. So, here's a new book:
let mut book = new;
// "What are are all of these extra types?" — you must be wondering.
// Let me explain. You know what — I won't explain that right now.
// The important part is that we have a book.
// In that book, we can store accounts, units, transactions and moves.
// And doing all of that, is quite simple. So let's get to it.
// Let's start by adding an account for some income channel:
let income_key = book.new_account;
// "What's that extra..." — we will get to that. Trust me.
// The important part is that we have an account.
// Actually, _the book_ has an account. What we own is an account key.
// We will later use this key to reference this account.
// It's nice that we have an account, so let's have another one!
let bank_key = book.new_account;
// And now, that we have two accounts, we can move money around.
// Which is exciting. I know. But, actually, we can't do that yet.
// Because we don't have any units. So let's talk about units.
// Units may represent currencies. Or cryptocurrencies. Or units of
// distance or volume... But if we're honest, they will usually
// represent some form of money. Yet, it's not this crate's scope to
// make such decisions. As far as this crate is concerned — they're just
// units. We'll talk more about this later.
let usd_key = book.new_unit;
// Look — we have dollars! US dollars (USD). And that `()` argument —
// thank you for being patient and not mentioning it.
// Now that we have two accounts and a unit, we can move money around.
// Which is exciting. I know. But, actually, we can't do that yet.
// Because, in this crate moving money around is represented by _moves_.
// So, we know that we need a move. But... moves are not directly inside
// a book — they live inside _transactions_. So we'll make a transaction
// to hold the move:
book.insert_transaction;
// That `0` argument is the index in which to insert the transaction
// into the book. You see, a book holds a single ordered collection of
// transactions. So... this created a new transaction and inserted it
// into the book at index `0`.
// So the book now has two accounts, one unit and one transaction.
// So, now we can move money around. Which is exciting. I know.
// But, actually, we can't do that yet. Cool motif, huh? We now need a
// _sum_. "A what—now?" you ask? A sum. Look:
let mut sum = new;
sum.set_amount_for_unit;
// We have created a sum and set the amount of a specific unit in it.
// "Wait — support for multiple units?" (that's you, asking).
// Yes. Sums support multiple units. Thank Joe for that. It's his idea.
// We'll get to using multiple units later. For now, this sum represents
// a hundred USD.
// So now the book contains two accounts, one unit and one transaction
// and also a sum that we own directly. So now we can move money around.
// Exciting, isn't it? And this is as far as this motif goes.
// Because now we really can move money around. Look:
book.insert_move;
// What this did is created a new move and inserted it into the existing
// transaction that is at index `0`. We only have one transaction, so
// that's where it is. And the move was inserted at index `0` in the
// transaction. You see — moves in a transaction are orderd. So, it's
// kind of like this:
//
// - book
// 0. transaction
// 0. move
//
// The move is of 100 dollars from the income account and to the bank
// account. What a miracle...
// As you may have guessed, you may add more accounts, units,
// transactions and moves. Here are three points to get in your mind.
// You may know best how to do that.
// - Accounts and units are referenced by keys.
// - Transactions and moves are referenced by indexes.
// - The index of a transaction is its index in the whole book.
// - The index of a move is its index in the transaction it's inside of.
// OK, those are actually four points.
// At this point (no pun intended), we would like to see the balance of
// the accounts. Well, I would — and I'm writing this tutorial, so:
let balance = book.account_balance_at_transaction;
assert_eq!;
let balance = book.account_balance_at_transaction;
assert_eq!;
// Cool?
// Let's move more money around, just to confirm our understanding:
let wallet_key = book.new_account;
// This created a new account that represents a wallet.
book.insert_transaction;
// This created a new empty transaction and inserted it at index `1`.
let mut sum = new;
sum.set_amount_for_unit;
book.insert_move;
// Created and inserted a move of 100 USD from the bank account to the
// wallet account. Isn't this fun?
// Now, let's see some balances, using the index of this most recent
// transaction:
let balance = book.account_balance_at_transaction;
assert_eq!;
let balance = book.account_balance_at_transaction;
assert_eq!;
let balance = book.account_balance_at_transaction;
assert_eq!;
// Now, let's insert a new transaction between the two existing ones:
book.insert_transaction;
let mut sum = new;
sum.set_amount_for_unit;
book.insert_move;
// And look at a running balance of the bank account:
let bank_running_balance: =
.iter
.map
.collect;
assert_eq!;
// So far, we've learned a few methods that insert data into the book
// and one that calculates a balance. You may have noticed that in order
// to call [Book::account_balance_at_transaction], we need to have both
// a key of an existing account and an index of an existing transaction.
// So, how can we obtain these? This way for accounts and units:
let _accounts: =
book.accounts.collect;
let _units: = book.units.collect;
// Note that the order of the iterator returned from [Book::accounts] is
// undefined. And this way for transactions:
let _transactions: =
book.transactions.enumerate.collect;
Metadata
use *;
// It's probably time to explain all those `()` arguments that we've so
// far been patient regarding. This crate allows arbitrary data to be
// attached/added/included in the book itself and all records in it:
// accounts, units, moves and transactions.
//
// When creating a book, the _types_ of these metadata must be provided.
// So far, `()` has been provided as the metadata type for all records.
// Let's define some non-`()` metadata types:
let mut book: =
new;
// In order, the types of metadata that are defined in this example are:
//
// - For the book itself, just a `u8`. Perhaps in your system, books are
// identified with merely an integer.
// - For each account, there's an `id` of a `u8` and a `name` of a
// `&'static str`.
// - With units, we'd like to represent currencies.
// - For moves, we seem to not require metadata in this example.
// - Transactions have merely a `&str` that perhaps is used as a note.
//
// Now, let's see how these metadata types are used:
assert_eq!;
// Alright!
let wallet_key = book.new_account;
let bank_key = book.new_account;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
// Cool!
let usd_key = book.new_unit;
assert_eq!;
assert_eq!;
// Sweet!
book.insert_transaction;
assert_eq!;
// Rad!
book.insert_move;
assert_eq!;
// Dope!