Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Welcome to the kakei documentation!

kakei is a command-line application for managing personal finances using the Japanese kakeibo (家計簿) method. It provides transaction tracking, categorization, and powerful Lisp-based table transformations for flexible analysis and reporting.

What is Kakeibo?

Kakeibo (家計簿) is a Japanese household financial ledger system that emphasizes mindful spending and saving. The word combines:

  • 家計 (kakei): household finances
  • 簿 (bo): ledger/account book

Key Features

  • 📊 Transaction Management: Add, list, and manage financial transactions
  • 🏷️ Category & Account Organization: Organize transactions by customizable categories and accounts
  • 🔄 Lisp-Based Transformations: Transform and analyze transaction data using a small Lisp dialect
  • 📋 Table Display: Beautiful table formatting using the tabled crate
  • 💾 SQLite Database: Persistent storage with automatic migrations
  • ⚙️ Configuration: Customizable categories and accounts

Why kakei?

Traditional financial tracking tools can be rigid and inflexible. kakei takes a different approach:

  1. Flexible Data Analysis: Use Lisp expressions to transform and analyze your financial data in any way you need
  2. Command-Line First: Designed for developers and power users who prefer the command line
  3. Open and Portable: Your data is stored in a standard SQLite database that you fully control
  4. Extensible: The Lisp-based transformation system allows for unlimited customization

Getting Started

If you're new to kakei, we recommend:

  1. Start with the Installation guide to get kakei installed on your system
  2. Follow the Quick Start guide to initialize your database and add your first transactions
  3. Explore the Commands reference to learn all available commands
  4. Learn about Lisp Functions to unlock powerful data transformations

Contributing

kakei is open source and welcomes contributions! See the Contributing guide for more information.

License

kakei is licensed under the MIT License. See the repository's LICENSE file for details.

Author

haruki7049 tontonkirikiri@gmail.com

Repository

https://github.com/haruki7049/kakei

Installation

This guide covers all the ways you can install kakei on your system.

Choose the installation method that works best for you:

Verifying Installation

After installation, verify that kakei is correctly installed:

kakei --help

You should see the help message with available commands.

Next Steps

Now that you have kakei installed, proceed to the Quick Start guide to initialize your database and start tracking your finances!

Prerequisites

Before installing kakei, ensure you have:

  • Rust 1.91.1 or later (for building from source)
  • SQLite 3 (for database functionality)

Build from Source

Building from source gives you the latest version and allows you to customize the build:

git clone https://github.com/haruki7049/kakei.git
cd kakei
cargo build --release

The binary will be available at target/release/kakei. You can copy it to a directory on your PATH (e.g., /usr/local/bin) if desired:

sudo cp target/release/kakei /usr/local/bin/

Install via Cargo

The easiest way to install kakei if you already have Rust installed:

From the repository root:

cargo install --path .

This installs kakei to your cargo bin directory (usually ~/.cargo/bin).

Make sure ~/.cargo/bin is in your PATH:

export PATH="$HOME/.cargo/bin:$PATH"

Binary Releases

Pre-built binaries may be available for download on the GitHub Releases page.

To install from a release:

  1. Download the appropriate archive for your platform
  2. Extract the kakei binary
  3. Move it to a directory on your PATH (e.g., /usr/local/bin/)
  4. Make it executable: chmod +x /usr/local/bin/kakei

Install via Nix

If you have Nix with flakes enabled, you can install kakei directly from the repository:

Quick Run (without installing)

# Run directly without installing
nix run github:haruki7049/kakei -- --help

Install to Profile

# Install from the flake
nix profile install github:haruki7049/kakei

Development Environment

For local development, the flake provides a development shell:

# Enter development shell with Rust toolchain and dependencies
nix develop

The flake provides:

  • packages.default: The kakei binary
  • devShells.default: Development environment with Rust toolchain and dependencies

Platform Notes

Linux

On Linux, kakei follows the XDG Base Directory Specification:

  • Configuration: ~/.config/kakei/ (or $XDG_CONFIG_HOME/kakei/)
  • Data: ~/.local/share/kakei/ (or $XDG_DATA_HOME/kakei/)

macOS

On macOS, if XDG environment variables are not set, kakei will use:

  • Configuration: ~/Library/Application Support/kakei/
  • Data: ~/Library/Application Support/kakei/

Windows

On Windows, if XDG environment variables are not set, kakei will use:

  • Configuration: %APPDATA%\kakei\
  • Data: %APPDATA%\kakei\

Verifying Installation

After installation, verify that kakei is correctly installed:

kakei --help

You should see the help message with available commands.

Next Steps

Now that you have kakei installed, proceed to the Quick Start guide to initialize your database and start tracking your finances!

Quick Start

This guide will walk you through the basic workflow of using kakei to manage your finances.

Initialize Database

Before you can start tracking transactions, you need to initialize the database:

kakei init

This command:

  • Creates the database file at the appropriate location for your platform
    • Linux (XDG): ~/.local/share/kakei/kakei.db
    • macOS: ~/Library/Application Support/kakei/kakei.db
    • Windows: %APPDATA%\kakei\kakei.db
  • Runs database migrations to set up the schema
  • Initializes default categories: Food, Transport, Daily Goods, Hobby, Salary
  • Initializes default accounts: Cash, Bank

You only need to run this command once when you first start using kakei.

Add Transactions

Now you can start adding financial transactions. The kakei add command requires several pieces of information:

Basic Expense

Record a simple expense:

kakei add --date 2025-01-01 --amount -1000 --category Food --account Cash

Expense with Memo

Add a note to help remember what the transaction was for:

kakei add --date 2025-01-02 --amount -2000 --category Transport --account Cash --memo "Train pass"

Recording Income

Positive amounts represent income:

kakei add --date 2025-01-15 --amount 50000 --category Salary --account Bank --memo "Monthly salary"

Different Currency

By default, kakei uses JPY (Japanese Yen), but you can specify other currencies:

kakei add --date 2025-01-20 --amount -50 --category Food --account Cash --currency USD

Understanding Amounts

The --amount parameter expects values in the currency's minor units as an integer:

  • For JPY (no subunits): Use integer yen

    • -1000 represents ¥-1,000 (expense)
    • 50000 represents ¥50,000 (income)
  • For USD, EUR, etc.: Use cents

    • -1050 represents $-10.50 (expense)
    • 50000 represents $500.00 (income)

Negative amounts = expenses (money going out)
Positive amounts = income (money coming in)

List Transactions

View your recent transactions in a formatted table:

kakei list

Example output:

╭────────────┬────────┬───────────┬─────────┬──────────────╮
│ Date       │ Amount │ Category  │ Account │ Memo         │
├────────────┼────────┼───────────┼─────────┼──────────────┤
│ 2025-01-15 │ ¥50000 │ Salary    │ Bank    │ Monthly sal… │
│ 2025-01-02 │ ¥-2000 │ Transport │ Cash    │ Train pass   │
│ 2025-01-01 │ ¥-1000 │ Food      │ Cash    │              │
╰────────────┴────────┴───────────┴─────────┴──────────────╯

The list command displays the 20 most recent transactions by default, showing:

  • Date
  • Amount (with currency symbol)
  • Category
  • Account
  • Memo

Transform with Lisp

One of kakei's most powerful features is the ability to transform and analyze your transaction data using Lisp expressions.

View All Transactions

kakei transform --program "table"

The table variable contains all your transactions as a Lisp data structure.

Group by Category

See spending broken down by category:

kakei transform --program "(group-by table (lambda (pair) (cdr (assoc 'category (cdr pair)))))"

Group by Account

See transactions grouped by account:

kakei transform --program "(group-by table (lambda (pair) (cdr (assoc 'account (cdr pair)))))"

Get First Transaction

kakei transform --program "(cons (car table) ())"

Skip First Transaction

kakei transform --program "(cdr table)"

Get First Two Transactions

kakei transform --program "(cons (car table) (cons (car (cdr table)) ()))"

Commands

Complete reference for all kakei commands.

  • init - Initialize the database and configuration
  • add - Add a new financial transaction
  • list - List recent transactions
  • transform - Transform and analyze data with Lisp

init

Initialize the database and configuration.

Usage

kakei init

Description

The init command sets up kakei for first-time use:

  1. Creates the database file at the platform-appropriate location:

    • Linux (XDG): ~/.local/share/kakei/kakei.db
    • macOS: ~/Library/Application Support/kakei/kakei.db
    • Windows: %APPDATA%\kakei\kakei.db
  2. Runs database migrations to create the necessary tables and schema

  3. Initializes default categories:

    • Food
    • Transport
    • Daily Goods
    • Hobby
    • Salary
  4. Initializes default accounts:

    • Cash
    • Bank

When to Use

  • Run this command once when you first install kakei
  • If you delete your database and want to start fresh
  • After moving kakei to a new system

Example

$ kakei init
Database initialized successfully!

add

Add a new financial transaction to the database.

Usage

kakei add --date <DATE> --amount <AMOUNT> --category <CATEGORY> --account <ACCOUNT> [OPTIONS]

Required Arguments

  • --date <DATE>

    • Transaction date in YYYY-MM-DD format
    • Example: 2025-01-15
  • --amount <AMOUNT>

    • Transaction amount in minor units (integer)
    • Negative for expenses (money out)
    • Positive for income (money in)
    • Example: -1000 (¥-1,000 expense) or 50000 (¥50,000 income)
  • --category <CATEGORY>

    • Category name for the transaction
    • Must match one of your configured categories
    • Example: Food, Transport, Salary
  • --account <ACCOUNT>

    • Account name for the transaction
    • Must match one of your configured accounts
    • Example: Cash, Bank, Card

Optional Arguments

  • --currency <CURRENCY>

    • Currency code (default: JPY)
    • Example: USD, EUR, GBP
  • --memo <MEMO>

    • Optional note or description
    • Example: "Monthly train pass"

Examples

Simple Expense

kakei add --date 2025-01-01 --amount -1000 --category Food --account Cash

Expense with Memo

kakei add --date 2025-01-02 --amount -2000 --category Transport --account Cash --memo "Monthly train pass"

Recording Income

kakei add --date 2025-01-15 --amount 50000 --category Salary --account Bank

USD Transaction

kakei add --date 2025-01-20 --amount -5000 --category Food --account Cash --currency USD --memo "Dinner in NYC"

Amount Format

The --amount parameter uses minor units (the smallest subdivision of a currency):

CurrencyMinor UnitExample InputActual Value
JPYYen (no subunit)-1000¥-1,000
USDCents-1050$-10.50
EURCents-2099€-20.99
GBPPence-1500£-15.00

list

Display recent transactions in a formatted table.

Usage

kakei list

Description

The list command displays your 20 most recent transactions in a beautifully formatted table with rounded borders.

Output Format

╭────────────┬────────┬───────────┬─────────┬──────────────╮
│ Date       │ Amount │ Category  │ Account │ Memo         │
├────────────┼────────┼───────────┼─────────┼──────────────┤
│ 2025-01-15 │ ¥50000 │ Salary    │ Bank    │ Monthly sal… │
│ 2025-01-02 │ ¥-2000 │ Transport │ Cash    │ Train pass   │
│ 2025-01-01 │ ¥-1000 │ Food      │ Cash    │              │
╰────────────┴────────┴───────────┴─────────┴──────────────╯

Columns

  • Date: Transaction date (YYYY-MM-DD)
  • Amount: Formatted amount with currency symbol
  • Category: Transaction category
  • Account: Account used
  • Memo: Optional note (truncated if long)

Example

$ kakei list
╭────────────┬────────┬───────────┬─────────┬──────────────╮
│ Date       │ Amount │ Category  │ Account │ Memo         │
├────────────┼────────┼───────────┼─────────┼──────────────┤
│ 2025-01-15 │ ¥50000 │ Salary    │ Bank    │ Monthly sal… │
│ 2025-01-02 │ ¥-2000 │ Transport │ Cash    │ Train pass   │
│ 2025-01-01 │ ¥-1000 │ Food      │ Cash    │              │
╰────────────┴────────┴───────────┴─────────┴──────────────╯

transform

Transform and analyze transaction data using Lisp programs.

Usage

kakei transform --program <LISP_PROGRAM>

Required Arguments

  • --program <LISP_PROGRAM>
    • A Lisp expression that transforms the transaction table
    • The variable table contains all transactions
    • See Lisp Functions for available functions

Description

The transform command is kakei's most powerful feature. It allows you to:

  • Filter transactions based on criteria
  • Group transactions by category, account, or custom logic
  • Perform calculations and aggregations
  • Extract specific transaction fields
  • Create custom reports

The transaction data is provided as a Lisp data structure in the table variable.

Examples

View All Transactions

kakei transform --program "table"

Group by Category

kakei transform --program "(group-by table (lambda (pair) (cdr (assoc 'category (cdr pair)))))"

Group by Account

kakei transform --program "(group-by table (lambda (pair) (cdr (assoc 'account (cdr pair)))))"

Get First Transaction Only

kakei transform --program "(cons (car table) ())"

Skip First Transaction

kakei transform --program "(cdr table)"

Get First Two Transactions

kakei transform --program "(cons (car table) (cons (car (cdr table)) ()))"

Shell Quoting

Be careful with shell quoting when passing Lisp programs:

POSIX shells:

kakei transform --program "(group-by table (lambda (pair) (cdr (assoc 'category (cdr pair)))))"

PowerShell:

kakei transform --program '(group-by table (lambda (pair) (cdr (assoc ''category'' (cdr pair)))))'

See Also

  • Data Format - Understanding transaction data structure
  • Lisp Functions - Complete reference of available Lisp functions
  • Examples - Real-world transformation examples

Data Format

Understanding how kakei represents transaction data internally is essential for effective use of the transform command.

Transaction Structure

Each transaction in kakei is represented as an association list (alist) in Lisp format. An association list is a list of key-value pairs.

Single Transaction Example

(ID-001 . ((date . "2025-01-01")
           (amount . -1000)
           (category . "Food")
           (account . "Cash")
           (memo . "")))

Transaction Fields

Each transaction contains the following fields:

FieldTypeDescriptionExample
IDStringUnique transaction identifier"ID-001"
dateStringTransaction date (YYYY-MM-DD)"2025-01-01"
amountIntegerAmount in minor units-1000 (¥-1,000)
categoryStringCategory name"Food"
accountStringAccount name"Cash"
memoStringOptional note (empty string if none)"Train pass"

Understanding the Structure

The transaction is a pair (cons cell):

  • Car (first element): Transaction ID
  • Cdr (second element): Association list of fields
(ID-001 . field-list)

The field list is itself a list of pairs:

((date . "2025-01-01")
 (amount . -1000)
 (category . "Food")
 (account . "Cash")
 (memo . ""))

Table Structure

The table variable provided to transform commands contains a list of transactions:

((ID-001 . ((date . "2025-01-01") (amount . -1000) ...))
 (ID-002 . ((date . "2025-01-02") (amount . -2000) ...))
 (ID-003 . ((date . "2025-01-15") (amount . 50000) ...))
 ...)

Table Operations

Access First Transaction

(car table)

Returns:

(ID-001 . ((date . "2025-01-01") (amount . -1000) ...))

Access Remaining Transactions

(cdr table)

Returns a table without the first transaction.

Get Transaction ID

(car (car table))

or

(car (car table))

Get Transaction Fields

(cdr (car table))

Returns the association list of fields for the first transaction.

Lisp Functions

The kakei_lisp dialect provides a set of built-in functions for transforming and analyzing transaction data. This reference covers all available functions.

Core Functions

lambda

Create anonymous (unnamed) functions.

Syntax:

(lambda (parameter ...) body)

Examples:

; Function that adds 1 to its argument
(lambda (x) (+ x 1))

; Function that gets the category from a transaction
(lambda (pair) (cdr (assoc 'category (cdr pair))))

Usage in transform:

kakei transform --program "(group-by table (lambda (pair) (cdr (assoc 'category (cdr pair)))))"

define

Define variables or functions for reuse within a program.

Syntax:

(define name value)

Examples:

; Define a constant
(define pi 3.14159)

; Define a function
(define (double x) (+ x x))

; Use the defined function
(double 5)  ; => 10

if

Conditional evaluation - execute different code based on a condition.

Syntax:

(if condition then-expr else-expr)

Examples:

; Check if a value is empty
(if (null? x) "empty" "not empty")

; Check equality
(if (equal? category "Food") "food-related" "other")

List Operations

cons

Construct a pair (cons cell) - the fundamental building block of Lisp lists.

Syntax:

(cons first second)

Examples:

(cons 1 2)           ; => (1 . 2)
(cons 1 ())          ; => (1)
(cons 1 (cons 2 ())) ; => (1 2)

Usage:

; Create a list with the first transaction only
(cons (car table) ())

car

Get the first element of a pair.

Syntax:

(car pair)

Examples:

(car (cons 1 2))     ; => 1
(car '(1 2 3))       ; => 1
(car table)          ; => first transaction

Usage:

# Get the first transaction
kakei transform --program "(car table)"

cdr

Get the second element of a pair (everything after the first element).

Syntax:

(cdr pair)

Examples:

(cdr (cons 1 2))     ; => 2
(cdr '(1 2 3))       ; => (2 3)
(cdr table)          ; => all transactions except first

Usage:

# Skip the first transaction
kakei transform --program "(cdr table)"

Comparison Functions

equal?

Test if two values are equal.

Syntax:

(equal? value1 value2)

Examples:

(equal? "Food" "Food")      ; => #t (true)
(equal? "Food" "Transport") ; => #f (false)
(equal? 100 100)            ; => #t
(equal? 100 200)            ; => #f

Usage:

; Filter transactions where category is "Food"
(if (equal? (cdr (assoc 'category (cdr pair))) "Food")
    pair
    ())

null?

Test if a value is nil (empty).

Syntax:

(null? value)

Examples:

(null? ())           ; => #t (true)
(null? '(1 2 3))     ; => #f (false)
(null? "")           ; => #f (false - empty string is not nil)

Usage:

; Check if we've reached the end of a list
(if (null? remaining-transactions)
    "No more transactions"
    "More transactions exist")

Association List Functions

assoc

Find a key in an association list and return the key-value pair.

Syntax:

(assoc key alist)

Examples:

(assoc 'category '((date . "2025-01-01") (category . "Food")))
; => (category . "Food")

(assoc 'amount '((date . "2025-01-01") (amount . -1000)))
; => (amount . -1000)

Usage:

# Get the category field from first transaction
kakei transform --program "(assoc 'category (cdr (car table)))"

To get just the value (not the pair), combine with cdr:

# Get the category value
kakei transform --program "(cdr (assoc 'category (cdr (car table))))"

Common Pattern: Extracting Field Values

; Get category of a transaction
(cdr (assoc 'category (cdr transaction)))

; Get amount of a transaction
(cdr (assoc 'amount (cdr transaction)))

; Get date of a transaction
(cdr (assoc 'date (cdr transaction)))

; Get account of a transaction
(cdr (assoc 'account (cdr transaction)))

; Get memo of a transaction
(cdr (assoc 'memo (cdr transaction)))

Table Manipulation

group-by

Group a table of transactions by a key function. This is one of the most powerful functions for analysis.

Syntax:

(group-by table key-fn)

Parameters:

  • table: The list of transactions
  • key-fn: A lambda function that extracts the grouping key from each transaction

Returns: A list of groups, where each group is:

("GroupName" (transaction1) (transaction2) ...)

Examples:

Group by Category

kakei transform --program "(group-by table (lambda (pair) (cdr (assoc 'category (cdr pair)))))"

Output structure:

(("Food" 
  (ID-001 . ((date . "2025-01-01") (amount . -1000) ...)))
 ("Transport"
  (ID-002 . ((date . "2025-01-02") (amount . -2000) ...)))
 ("Salary"
  (ID-003 . ((date . "2025-01-15") (amount . 50000) ...))))

Group by Account

kakei transform --program "(group-by table (lambda (pair) (cdr (assoc 'account (cdr pair)))))"

Group by Date

kakei transform --program "(group-by table (lambda (pair) (cdr (assoc 'date (cdr pair)))))"

How it Works:

For each transaction in the table:

  1. The lambda function extracts a grouping key (e.g., category name)
  2. Transactions with the same key are grouped together
  3. The result is a list of groups labeled by their key

Configuration

Learn how to configure kakei to match your financial tracking needs.

Configuration File

kakei uses a TOML configuration file to customize default categories and accounts.

Location

The configuration file location depends on your operating system:

  • Linux (XDG): ~/.config/kakei/config.toml (or $XDG_CONFIG_HOME/kakei/config.toml)
  • macOS: ~/Library/Application Support/kakei/config.toml
  • Windows: %APPDATA%\kakei\config.toml

Format

The configuration file uses TOML format:

default_categories = ["Food", "Transport", "Daily Goods", "Hobby", "Salary"]
default_accounts = ["Cash", "Bank"]

Creating a Configuration File

kakei automatically creates a default configuration when you run kakei init. If you want to customize it:

  1. Locate the config file (see locations above)

  2. Edit with your favorite text editor:

    # Linux/macOS
    nano ~/.config/kakei/config.toml
    
    # Or use any editor you prefer
    vim ~/.config/kakei/config.toml
    code ~/.config/kakei/config.toml
    
  3. Add your custom categories and accounts

Example Configurations

Personal Finance Tracking

default_categories = [
    "Food",
    "Transport",
    "Housing",
    "Utilities",
    "Healthcare",
    "Entertainment",
    "Shopping",
    "Salary",
    "Freelance",
    "Investment"
]

default_accounts = [
    "Cash",
    "Bank",
    "Credit Card",
    "Savings",
    "Investment Account"
]

Business Expense Tracking

default_categories = [
    "Office Supplies",
    "Software",
    "Travel",
    "Meals",
    "Marketing",
    "Payroll",
    "Revenue",
    "Consulting"
]

default_accounts = [
    "Business Checking",
    "Business Savings",
    "Business Credit Card",
    "Petty Cash"
]

Minimal Setup

default_categories = ["Expense", "Income"]
default_accounts = ["Main"]

Database Location

The SQLite database file stores all your transaction data.

Default Location

  • Linux (XDG): ~/.local/share/kakei/kakei.db (or $XDG_DATA_HOME/kakei/kakei.db)
  • macOS: ~/Library/Application Support/kakei/kakei.db
  • Windows: %APPDATA%\kakei\kakei.db

Customizing Database Location

Currently, kakei automatically determines the database location based on your operating system's conventions. To use a different location:

  1. Set the XDG_DATA_HOME environment variable (Linux):

    export XDG_DATA_HOME=/path/to/custom/location
    
  2. Run kakei commands - they will now use the custom location

Backing Up Your Database

Since your financial data is stored in a single SQLite file, backing up is simple:

# Linux/macOS
cp ~/.local/share/kakei/kakei.db ~/backup/kakei-backup-$(date +%Y%m%d).db

# Or create a scheduled backup script
#!/bin/bash
BACKUP_DIR=~/kakei-backups
mkdir -p "$BACKUP_DIR"
cp ~/.local/share/kakei/kakei.db "$BACKUP_DIR/kakei-$(date +%Y%m%d-%H%M%S).db"

Database Schema

The database is a standard SQLite database. You can inspect it directly using SQLite tools:

sqlite3 ~/.local/share/kakei/kakei.db

# Inside sqlite3:
.tables          # List tables
.schema          # Show schema
SELECT * FROM transactions LIMIT 5;  # Query transactions

Warning: Directly modifying the database outside of kakei commands may cause data corruption. Always use kakei commands for data manipulation, and only use direct SQL for read-only queries.

Examples

Real-world examples of using kakei for various financial tracking scenarios.

Monthly Budget Tracking

Track your monthly income and expenses to understand your budget.

Setup

First, add your transactions for the month:

# Add various expenses
kakei add --date 2025-01-05 --amount -1200 --category Food --account Cash --memo "Lunch"
kakei add --date 2025-01-10 --amount -3000 --category Transport --account Card --memo "Monthly pass"
kakei add --date 2025-01-12 --amount -500 --category Hobby --account Cash --memo "Book"
kakei add --date 2025-01-20 --amount -800 --category Food --account Cash --memo "Dinner"

# Add income
kakei add --date 2025-01-15 --amount 50000 --category Salary --account Bank --memo "January salary"

View All Transactions

kakei list

Output:

╭────────────┬────────┬───────────┬─────────┬──────────────╮
│ Date       │ Amount │ Category  │ Account │ Memo         │
├────────────┼────────┼───────────┼─────────┼──────────────┤
│ 2025-01-20 │ ¥-800  │ Food      │ Cash    │ Dinner       │
│ 2025-01-15 │ ¥50000 │ Salary    │ Bank    │ January sal… │
│ 2025-01-12 │ ¥-500  │ Hobby     │ Cash    │ Book         │
│ 2025-01-10 │ ¥-3000 │ Transport │ Card    │ Monthly pass │
│ 2025-01-05 │ ¥-1200 │ Food      │ Cash    │ Lunch        │
╰────────────┴────────┴───────────┴─────────┴──────────────╯

Group by Category

See spending breakdown by category:

kakei transform --program "(group-by table (lambda (pair) (cdr (assoc 'category (cdr pair)))))"

This shows all transactions grouped by their category, making it easy to see where your money is going.

Calculate Category Totals

While kakei's Lisp dialect doesn't have arithmetic functions yet, you can group by category and manually sum the amounts:

kakei transform --program "(group-by table (lambda (pair) (cdr (assoc 'category (cdr pair)))))"

Look at the amounts in each group to understand your spending patterns.

Analyzing Spending Patterns

Group by Account

See where your money is being spent:

kakei transform --program "(group-by table (lambda (pair) (cdr (assoc 'account (cdr pair)))))"

This helps you understand:

  • How much you're spending from each account
  • Whether you're using cash vs. card appropriately
  • If your bank balance matches your expectations

View Recent Transactions Only

Get just the most recent transaction:

kakei transform --program "(cons (car table) ())"

Get the two most recent transactions:

kakei transform --program "(cons (car table) (cons (car (cdr table)) ()))"

Skip Old Transactions

Skip the first (oldest) transaction:

kakei transform --program "(cdr table)"

This is useful when you want to exclude historical data from analysis.

Architecture

Understanding kakei's internal architecture helps developers contribute effectively and users understand how the application works.

Project Structure

kakei is organized as a Rust workspace with multiple crates:

kakei/
├── src/                      # Main CLI application
├── crates/
│   └── processor/
│       ├── src/              # Business logic and transformations
│       └── crates/
│           ├── database/     # Database layer (SQLite)
│           ├── money/        # Money type with currency support
│           └── klisp/        # Embedded Lisp dialect
├── tests/                    # Integration tests
├── book/                     # Documentation (mdBook)
└── Cargo.toml               # Workspace configuration

Crate Overview

kakei (Main Crate)

Location: src/
Purpose: Command-line interface and user interaction

Responsibilities:

  • Parse command-line arguments using clap
  • Handle user input and output
  • Coordinate between other crates
  • Format output tables using tabled
  • Manage configuration with confy

Key Files:

  • src/main.rs: Application entry point
  • src/commands/: Command implementations (init, add, list, transform)

kakei_processor

Location: crates/processor/
Purpose: Business logic and table transformations

Responsibilities:

  • Transaction management logic
  • Table transformation orchestration
  • Integration between database and Lisp interpreter
  • Data conversion between formats

Dependencies:

  • kakei_database: For data persistence
  • kakei_lisp: For Lisp evaluation
  • kakei_money: For money types

kakei_database

Location: crates/processor/crates/database/
Purpose: Database layer with SQLite

Responsibilities:

  • SQLite database connection management
  • Transaction CRUD operations
  • Database migrations
  • Query execution

Key Technologies:

  • sqlx: Async SQLite driver with compile-time query verification
  • chrono: Date/time handling

Schema:

CREATE TABLE transactions (
    id TEXT PRIMARY KEY,
    date TEXT NOT NULL,
    amount INTEGER NOT NULL,
    category TEXT NOT NULL,
    account TEXT NOT NULL,
    currency TEXT NOT NULL DEFAULT 'JPY',
    memo TEXT
);

kakei_money

Location: crates/processor/crates/money/
Purpose: Money type with currency support

Responsibilities:

  • Type-safe money representation
  • Currency handling (JPY, USD, EUR, etc.)
  • Amount formatting with proper symbols
  • Minor unit conversion

Key Types:

#![allow(unused)]
fn main() {
pub struct Money {
    amount: i64,        // Amount in minor units
    currency: Currency, // Currency type
}

pub enum Currency {
    JPY,
    USD,
    EUR,
    GBP,
    // ...
}
}

Features:

  • Amounts stored in minor units (cents, yen, etc.)
  • Currency-specific formatting
  • Decimal conversion utilities

kakei_lisp

Location: crates/processor/crates/klisp/
Purpose: Embedded Lisp dialect for data transformation

Responsibilities:

  • Lisp parsing (using nom parser combinator)
  • Expression evaluation
  • Built-in function implementations
  • Runtime environment

Language Features:

  • S-expressions: (func arg1 arg2)
  • Lambda functions: (lambda (x) (+ x 1))
  • Core functions: cons, car, cdr, if, define
  • Association lists: assoc
  • Table manipulation: group-by

Architecture:

Input String
    ↓
Parser (nom)
    ↓
AST (Abstract Syntax Tree)
    ↓
Evaluator
    ↓
Result Value

Data Flow

Adding a Transaction

User Command
    ↓
CLI (kakei)
    ↓
Parse Arguments (clap)
    ↓
Create Transaction Object
    ↓
Processor (kakei_processor)
    ↓
Database Layer (kakei_database)
    ↓
SQLite Database

Listing Transactions

User Command
    ↓
CLI (kakei)
    ↓
Processor (kakei_processor)
    ↓
Database Layer (kakei_database)
    ↓
Query SQLite
    ↓
Convert to Money Types (kakei_money)
    ↓
Format as Table (tabled)
    ↓
Display to User

Transform Command

User Command with Lisp Program
    ↓
CLI (kakei)
    ↓
Processor (kakei_processor)
    ↓
Database: Load Transactions
    ↓
Convert to Lisp Data Structure
    ↓
Lisp Interpreter (kakei_lisp)
    ↓
Parse Lisp Program
    ↓
Evaluate Against Transaction Data
    ↓
Convert Result Back
    ↓
Format as Table
    ↓
Display to User

Key Technologies

Rust Edition

  • Rust 2024 Edition (Edition 2024)
  • Minimum Rust Version: 1.91.1

Major Dependencies

CrateVersionPurpose
clap4.4.11Command-line argument parsing
sqlx0.8.6Async SQLite with compile-time checks
tokio1.48.0Async runtime
chrono0.4.42Date and time handling
tabled0.20.0Table formatting
nom8.0.0Parser combinators for Lisp
serde1.0.219Serialization/deserialization
confy0.6.1Configuration management
directories6.0.0Platform-appropriate directories
xdg3.0.0XDG Base Directory support

Testing

  • assert_cmd: CLI testing
  • predicates: Assertion helpers
  • tempfile: Temporary files for tests

Nix Integration

kakei includes Nix flakes support for reproducible builds:

  • flake.nix: Flake definition
  • flake.lock: Locked dependencies
  • default.nix: Traditional Nix build
  • shell.nix: Development shell

Design Patterns

Workspace Organization

The workspace structure allows:

  • Independent testing of each component
  • Clear separation of concerns
  • Reusable components (e.g., kakei_money could be used elsewhere)
  • Faster incremental builds

Error Handling

kakei uses thiserror for custom error types:

#![allow(unused)]
fn main() {
#[derive(Error, Debug)]
pub enum KakeiError {
    #[error("Database error: {0}")]
    Database(#[from] sqlx::Error),
    
    #[error("Configuration error: {0}")]
    Config(String),
    
    // ...
}
}

Async/Await

Database operations are async using tokio:

#[tokio::main]
async fn main() -> Result<()> {
    // Async operations
}

Type Safety

Strong typing throughout:

  • Money type ensures currency safety
  • Transaction structs validate data
  • Compile-time SQL query verification

Configuration Management

Directory Resolution

Check XDG Environment Variables
    ↓ (if not set)
Platform-Specific Defaults
    ↓
Create Directory if Missing
    ↓
Load/Create Config File

Configuration Flow

confy crate
    ↓
Load config.toml
    ↓
Parse with TOML parser
    ↓
Deserialize to Config struct
    ↓
Use in Application

Database Management

Migrations

Migrations are handled at application startup:

  1. Check if database exists
  2. Create if missing
  3. Run pending migrations
  4. Initialize default data (categories, accounts)

Connection Pooling

SQLx provides connection pooling for efficient database access:

#![allow(unused)]
fn main() {
let pool = SqlitePool::connect(&database_url).await?;
}

Performance Considerations

Query Optimization

  • Indexes on frequently queried columns
  • Limit clauses for list operations (default: 20 transactions)
  • Efficient date-based queries

Memory Usage

  • Streaming results for large datasets
  • Lazy evaluation in Lisp interpreter
  • Efficient cons cell representation

Build Optimization

  • Release builds with optimizations enabled
  • Incremental compilation in workspace
  • Cargo cache for dependencies

Security Considerations

Data Safety

  • SQLite ACID properties ensure data integrity
  • Transactions for atomic operations
  • Regular backups recommended

Input Validation

  • Command-line argument validation via clap
  • SQL injection prevention via parameterized queries (sqlx)
  • Type-safe APIs throughout

File Permissions

  • Configuration and database files are user-readable/writable only
  • No sensitive data in configuration (categories/accounts only)

Future Architecture Considerations

Potential areas for expansion:

  1. Plugin System: Allow custom Lisp functions via plugins
  2. Multiple Databases: Support for multiple financial ledgers
  3. Import/Export: CSV/JSON import/export capabilities
  4. Web Interface: Optional web UI using the same core crates
  5. Sync: Cloud sync or multi-device support
  6. More Lisp Functions: Additional built-ins for calculations

Contributing to Architecture

When contributing, consider:

  1. Maintain separation of concerns - keep crates focused
  2. Add tests - especially for new Lisp functions
  3. Document public APIs - use Rust doc comments
  4. Follow existing patterns - match the codebase style
  5. Consider performance - especially for database operations

See Also

Development

Guide for developers working on kakei.

Running Tests

Run All Tests

cargo test --workspace

This runs:

  • Unit tests in each crate
  • Integration tests in tests/
  • Doc tests in documentation comments

Run Tests for Specific Crate

# Test the Lisp interpreter
cargo test --package kakei_lisp

# Test the database layer
cargo test --package kakei_database

# Test the processor
cargo test --package kakei_processor

Run with Output

See test output (normally hidden for passing tests):

cargo test -- --nocapture

Run Specific Test

cargo test test_name

Run Tests in Parallel

By default, Cargo runs tests in parallel. To run serially:

cargo test -- --test-threads=1

Doc Tests

Run only documentation tests:

cargo test --doc

Linting

Clippy

Run the Clippy linter for best practices and common mistakes:

cargo clippy --workspace

Fix warnings automatically (when possible):

cargo clippy --workspace --fix

Clippy with all warnings as errors:

cargo clippy --workspace -- -D warnings

Format Check

Check if code is properly formatted:

cargo fmt --check

Format Code

Auto-format all code:

cargo fmt

Building

Debug Build

For development, use debug builds (faster compilation, includes debug symbols):

cargo build

The binary will be at target/debug/kakei.

Release Build

For production use or performance testing:

cargo build --release

The binary will be at target/release/kakei.

Build Specific Crate

Build only a specific crate in the workspace:

# Build just the Lisp interpreter
cargo build --package kakei_lisp

# Build the database layer
cargo build --package kakei_database

Build All Workspace Members

cargo build --workspace

Contributing

Thank you for your interest in contributing to kakei! This guide will help you get started.

How to Build Locally

To build kakei locally for development:

Clone the Repository

git clone https://github.com/haruki7049/kakei.git
cd kakei

Build with Cargo

cargo build

Run Tests

cargo test --workspace

Run the Application

cargo run -- --help

For more details, see the Development guide.

Preview Documentation

To preview the documentation locally:

Install mdBook

cargo install mdbook

Build and Serve

cd docs/book
mdbook serve

Then open http://localhost:3000 in your browser.

Making Changes

  1. Edit the markdown files in docs/book/src/
  2. The documentation will automatically reload when you save changes
  3. Review your changes in the browser

For more information, see the docs/book/README.md.

Submitting Pull Requests

Before Submitting

  1. Fork the repository on GitHub
  2. Create a feature branch from main
  3. Make your changes with clear, focused commits
  4. Test your changes - run cargo test --workspace
  5. Lint your code - run cargo clippy --workspace
  6. Format your code - run cargo fmt

Creating the Pull Request

  1. Push your branch to your fork
  2. Open a pull request on GitHub
  3. Fill in the PR template with:
    • Description of changes
    • Related issues
    • Testing done
  4. Wait for review - maintainers will review your PR

After Submission

  • Respond to feedback - address review comments promptly
  • Keep your PR updated - rebase if needed
  • Be patient - reviews may take time

PR Guidelines

  • Keep PRs focused on a single feature or fix
  • Write clear commit messages
  • Update documentation if needed
  • Add tests for new functionality