Rust LogoBudget Planner

A Budget Planner is a financial tool designed to help individuals or organizations track, manage, and optimize their income and expenditures. The primary goal is to gain a clear understanding of where money comes from and where it goes, enabling informed financial decisions, promoting savings, and aiding in debt management or the achievement of specific financial goals.

Core Components of a Budget Planner:
1. Income Tracking: Recording all sources of money received, such as salaries, freelance payments, investments, gifts, etc.
2. Expense Tracking: Logging all money spent, typically categorized into different areas like housing, food, transportation, entertainment, utilities, and more. This categorization helps in identifying spending patterns.
3. Budgeting: Setting predetermined limits or allocations for spending in various categories over a specific period (e.g., monthly). This allows users to compare actual spending against planned spending.
4. Reporting and Analysis: Providing summaries, balances, and breakdowns of financial activity. This includes total income, total expenses, current balance, and expenditure per category.

How a Digital Budget Planner Works (Rust Example):
Our Rust example demonstrates a simple command-line budget planner with the following functionalities:
* Data Structures: It uses `struct`s like `Transaction` to represent individual financial movements (income or expense) with details such as type, category, amount, description, and timestamp. A `BudgetManager` struct holds a collection of these transactions.
* Categorization: `enum`s (`IncomeCategory`, `ExpenseCategory`) are used to define predefined categories, making data entry consistent. While the `Transaction` struct stores the category as a `String` for simplicity and flexibility, input validation ensures the user enters a recognized category.
* Transaction Management: The `BudgetManager` provides methods to add new transactions (income or expense) to a vector (`Vec`) of `Transaction` objects.
* Calculations: It calculates the total income, total expenses, and the current balance (income - expenses). It also summarizes income and expenses by their respective categories using a `HashMap`.
* User Interface: A simple command-line interface is implemented using `std::io` for reading user input (menu choices, transaction details) and displaying outputs.
* Timestamping: The `chrono` crate (an external dependency) is used to automatically assign a timestamp to each transaction, providing a record of when the financial event occurred.

Rust Specific Concepts Demonstrated:
* Structs and Enums: For defining custom data types and constrained choices.
* Vectors (`Vec`): To store dynamic collections of transactions.
* Hash Maps (`HashMap`): For efficient aggregation and summarization of data by category.
* Traits (`FromStr`): Implementing the `FromStr` trait for enums allows converting string input directly into enum variants, facilitating robust input parsing and validation.
* Error Handling (`Result`, `match`): Gracefully handling potential issues like invalid numerical input or unrecognized categories.
* Input/Output (`std::io`): Interacting with the user via the command line.
* Modularity: Organizing code into functions and methods for better structure and reusability.

Potential Enhancements:
For a production-ready application, one might consider adding features like data persistence (saving/loading transactions to a file or database), support for recurring transactions, setting actual budget limits for categories, more advanced reporting (e.g., monthly trends, visual charts), and a graphical user interface (GUI).

Example Code

```rust
use std::io::{self, Write};
use std::collections::HashMap;
use std::str::FromStr; // Import FromStr trait
use chrono::Local; // Import Local from chrono (Add `chrono = "0.4"` to your Cargo.toml)

// Enum for transaction types
#[derive(Debug, Clone, Copy, PartialEq)]
enum TransactionType {
    Income,
    Expense,
}

// Enum for income categories
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum IncomeCategory {
    Salary,
    Freelance,
    Investment,
    Gift,
    Other,
}

impl FromStr for IncomeCategory {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "salary" => Ok(IncomeCategory::Salary),
            "freelance" => Ok(IncomeCategory::Freelance),
            "investment" => Ok(IncomeCategory::Investment),
            "gift" => Ok(IncomeCategory::Gift),
            "other" => Ok(IncomeCategory::Other),
            _ => Err(format!("Unknown income category: {}", s)),
        }
    }
}

// Enum for expense categories
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum ExpenseCategory {
    Housing,
    Food,
    Transport,
    Entertainment,
    Utilities,
    Savings, // Can be an expense or transfer to savings account
    DebtPayment,
    Other,
}

impl FromStr for ExpenseCategory {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "housing" => Ok(ExpenseCategory::Housing),
            "food" => Ok(ExpenseCategory::Food),
            "transport" => Ok(ExpenseCategory::Transport),
            "entertainment" => Ok(ExpenseCategory::Entertainment),
            "utilities" => Ok(ExpenseCategory::Utilities),
            "savings" => Ok(ExpenseCategory::Savings),
            "debt" => Ok(ExpenseCategory::DebtPayment),
            "other" => Ok(ExpenseCategory::Other),
            _ => Err(format!("Unknown expense category: {}", s)),
        }
    }
}

// Struct to represent a single financial transaction
#[derive(Debug, Clone)]
struct Transaction {
    transaction_type: TransactionType,
    category: String, // Storing category as String for flexibility
    amount: f64,
    description: String,
    timestamp: String,
}

impl Transaction {
    fn new(transaction_type: TransactionType, category: String, amount: f64, description: String) -> Self {
        let now = Local::now();
        Self {
            transaction_type,
            category,
            amount,
            description,
            timestamp: now.format("%Y-%m-%d %H:%M:%S").to_string(),
        }
    }
}

// Struct to manage the budget
struct BudgetManager {
    transactions: Vec<Transaction>,
}

impl BudgetManager {
    fn new() -> Self {
        Self {
            transactions: Vec::new(),
        }
    }

    fn add_transaction(&mut self, transaction: Transaction) {
        self.transactions.push(transaction);
        println!("Transaction added successfully!");
    }

    fn get_total_income(&self) -> f64 {
        self.transactions.iter()
            .filter(|t| t.transaction_type == TransactionType::Income)
            .map(|t| t.amount)
            .sum()
    }

    fn get_total_expenses(&self) -> f64 {
        self.transactions.iter()
            .filter(|t| t.transaction_type == TransactionType::Expense)
            .map(|t| t.amount)
            .sum()
    }

    fn get_balance(&self) -> f64 {
        self.get_total_income() - self.get_total_expenses()
    }

    fn get_expenses_by_category(&self) -> HashMap<String, f64> {
        let mut category_totals: HashMap<String, f64> = HashMap::new();
        for transaction in &self.transactions {
            if transaction.transaction_type == TransactionType::Expense {
                *category_totals.entry(transaction.category.clone()).or_insert(0.0) += transaction.amount;
            }
        }
        category_totals
    }

    fn get_income_by_category(&self) -> HashMap<String, f64> {
        let mut category_totals: HashMap<String, f64> = HashMap::new();
        for transaction in &self.transactions {
            if transaction.transaction_type == TransactionType::Income {
                *category_totals.entry(transaction.category.clone()).or_insert(0.0) += transaction.amount;
            }
        }
        category_totals
    }

    fn display_summary(&self) {
        println!("\n--- Budget Summary ---");
        println!("Total Income:        ${:.2}", self.get_total_income());
        println!("Total Expenses:      ${:.2}", self.get_total_expenses());
        println!("Current Balance:     ${:.2}", self.get_balance());

        println!("\nIncome Breakdown by Category:");
        let income_by_category = self.get_income_by_category();
        if income_by_category.is_empty() {
            println!("  No income recorded yet.");
        } else {
            for (category, total) in income_by_category {
                println!("  {: <15}: ${:.2}", category, total);
            }
        }

        println!("\nExpense Breakdown by Category:");
        let expenses_by_category = self.get_expenses_by_category();
        if expenses_by_category.is_empty() {
            println!("  No expenses recorded yet.");
        } else {
            for (category, total) in expenses_by_category {
                println!("  {: <15}: ${:.2}", category, total);
            }
        }

        println!("\nAll Transactions:");
        if self.transactions.is_empty() {
            println!("  No transactions recorded yet.");
        } else {
            for t in &self.transactions {
                let sign = if t.transaction_type == TransactionType::Income { "+" } else { "-" };
                println!("  {} [{}] {} ${:.2} ({:?}) - {}", t.timestamp, t.category, sign, t.amount, t.transaction_type, t.description);
            }
        }
        println!("---------------------\n");
    }
}

// Helper function to read a line from stdin
fn read_line() -> String {
    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("Failed to read line");
    input.trim().to_string()
}

// Main application logic
fn main() {
    let mut manager = BudgetManager::new();

    loop {
        println!("Budget Planner Menu:");
        println!("1. Add Income");
        println!("2. Add Expense");
        println!("3. View Summary");
        println!("4. Exit");
        print!("Enter your choice: ");
        io::stdout().flush().expect("Failed to flush stdout");

        let choice = read_line();

        match choice.as_str() {
            "1" => {
                println!("\n--- Add Income ---");
                print!("Enter amount: ");
                io::stdout().flush().expect("Failed to flush stdout");
                let amount_str = read_line();
                let amount: f64 = match amount_str.parse() {
                    Ok(val) => val,
                    Err(_) => {
                        println!("Invalid amount. Please enter a number.");
                        continue;
                    }
                };

                println!("Available income categories: Salary, Freelance, Investment, Gift, Other");
                print!("Enter category: ");
                io::stdout().flush().expect("Failed to flush stdout");
                let category_str = read_line();
                // Validate category string against known income categories
                if IncomeCategory::from_str(&category_str).is_err() {
                    println!("Invalid income category. Please choose from: Salary, Freelance, Investment, Gift, Other.");
                    continue;
                }

                print!("Enter description (e.g., 'Monthly salary'): ");
                io::stdout().flush().expect("Failed to flush stdout");
                let description = read_line();

                let transaction = Transaction::new(TransactionType::Income, category_str, amount, description);
                manager.add_transaction(transaction);
            }
            "2" => {
                println!("\n--- Add Expense ---");
                print!("Enter amount: ");
                io::stdout().flush().expect("Failed to flush stdout");
                let amount_str = read_line();
                let amount: f64 = match amount_str.parse() {
                    Ok(val) => val,
                    Err(_) => {
                        println!("Invalid amount. Please enter a number.");
                        continue;
                    }
                };

                println!("Available expense categories: Housing, Food, Transport, Entertainment, Utilities, Savings, Debt, Other");
                print!("Enter category: ");
                io::stdout().flush().expect("Failed to flush stdout");
                let category_str = read_line();
                // Validate category string against known expense categories
                if ExpenseCategory::from_str(&category_str).is_err() {
                    println!("Invalid expense category. Please choose from: Housing, Food, Transport, Entertainment, Utilities, Savings, Debt, Other.");
                    continue;
                }

                print!("Enter description (e.g., 'Groceries for the week'): ");
                io::stdout().flush().expect("Failed to flush stdout");
                let description = read_line();

                let transaction = Transaction::new(TransactionType::Expense, category_str, amount, description);
                manager.add_transaction(transaction);
            }
            "3" => {
                manager.display_summary();
            }
            "4" => {
                println!("Exiting Budget Planner. Goodbye!");
                break;
            }
            _ => {
                println!("Invalid choice. Please enter a number between 1 and 4.");
            }
        }
    }
}
```