Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
107 changes: 107 additions & 0 deletions submissions/week-2/day-5/Abel-Osaretin/expense-tracker/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "expense-tracker"
version = "0.1.0"
edition = "2024"

[dependencies]
serde = {version = "1.0.228", features = ["derive"]}
serde_json = "1.0.149"
37 changes: 37 additions & 0 deletions submissions/week-2/day-5/Abel-Osaretin/expense-tracker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Expense Tracker

A simple CLI expense tracker built in Rust. It supports adding, viewing, updating, and removing expenses, and persists data to a JSON file between runs.

## Assignment Requirements Checklist

- Prints out operations a user can carry out.
- Program keeps running after reading, adding, updating, or deleting.
- User can quit the program with 'q' and is prompted y/n.
- After updating or adding, the user can see the values they just added.
- All interactions are written to a file (saved to JSON).
- Abstracted into modules.

## How It Works

- Startup loads existing data from `expenses.json`.
- Each expense has an `id`, `name`, `amount`, and `tx_type` (Credit/Debit).
- Data is saved back to `expenses.json` after add/update/delete and on quit.

## Run

```bash
cargo run
```

## Project Structure

- `src/main.rs`: application entry point and menu loop.
- `src/menu.rs`: menu options and selection logic.
- `src/modules.rs`: feature modules (add/view/update/delete/quit).
- `src/tracker.rs`: core tracker data model and persistence.
- `expenses.json`: persisted data file.

## Usage Tips

- Enter the menu number (1-4) or name (`add`, `view`, `update`, `remove`), or `q` to quit.
- On quit, confirm with `y` or `n`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"6": {
"id": 6,
"name": "Toy",
"amount": 22.0,
"tx_type": "Debit"
},
"3": {
"id": 3,
"name": "Food",
"amount": 345.0,
"tx_type": "Credit"
},
"4": {
"id": 4,
"name": "Meat",
"amount": 432.0,
"tx_type": "Debit"
},
"5": {
"id": 5,
"name": "Fish",
"amount": 677.0,
"tx_type": "Debit"
},
"1": {
"id": 1,
"name": "Cloth",
"amount": 555.0,
"tx_type": "Credit"
}
}
113 changes: 113 additions & 0 deletions submissions/week-2/day-5/Abel-Osaretin/expense-tracker/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
mod menu;
mod modules;
mod tracker;

use menu::Menu;
use std::io;
use tracker::ExpenseTracker;

fn main() {
let filename = "expenses.json";
let mut tracker = ExpenseTracker::new();

modules::username_module();
tracker.values = ExpenseTracker::load_from_file(filename);
if let Some(max_id) = tracker.values.keys().max() {
tracker.next_id = max_id + 1;
}

loop {
menu_module(&mut tracker, filename);
}
}

fn menu_module(tracker: &mut ExpenseTracker, filename: &str) {
let mut menu_input: String = String::new();

println!("Please select a menu number or name:");
println!(
"\n 1. {:#?}\n 2. {:#?}\n 3. {:#?}\n 4. {:#?}\n q. {:#?} ",
Menu::Add,
Menu::View,
Menu::Update,
Menu::Remove,
Menu::Quit
);

io::stdin()
.read_line(&mut menu_input)
.expect("Failed To Get Input");

let result = match Menu::menu_select(&menu_input) {
Some(result) => result,
None => {
println!("Invalid Menu");
return menu_module(tracker, filename);
}
};

match result {
Menu::Add => {
println!("-------------------------------------------------\n");
println!("Add Module");
println!("\n-------------------------------------------------\n");

let new_expenses = modules::add_module(tracker, filename);
tracker.save_to_file(filename);

println!("-------------------------------------------------");
println!("-------------------------------------------------");
println!("New {:#?}", new_expenses);
println!("-------------------------------------------------");
println!("-------------------------------------------------\n");
}
Menu::View => {
println!("-------------------------------------------------\n");
println!("View Module");
println!("\n-------------------------------------------------\n");
let all_expenses = modules::view_module(tracker);

println!("-------------------------------------------------");
println!("-------------------------------------------------");
println!("All Expenses: {:#?}", all_expenses);
println!("-------------------------------------------------");
println!("-------------------------------------------------\n");
}
Menu::Update => {
println!("-------------------------------------------------\n");
println!("Update Module");
println!("\n-------------------------------------------------\n");

let updated_data = modules::update_module(tracker, filename);
tracker.save_to_file(filename);

println!("-------------------------------------------------");
println!("-------------------------------------------------");
println!("Updated Data {:#?}", updated_data);
println!("-------------------------------------------------");
println!("-------------------------------------------------\n");
}
Menu::Remove => {
println!("-------------------------------------------------\n");
println!("\n-------------------------------------------------\n");
println!("Remove Module");

let result = modules::delete_module(tracker, filename);
tracker.save_to_file(filename);
println!("Expense Deleted: {result}");

println!("-------------------------------------------------");
println!("-------------------------------------------------\n");
}
Menu::Quit => {
println!("-------------------------------------------------\n");
println!("\n-------------------------------------------------\n");

println!("Quit Module");

modules::quit_module(tracker, filename);
println!("\n-------------------------------------------------\n");
println!("-------------------------------------------------\n");
}
}
}
20 changes: 20 additions & 0 deletions submissions/week-2/day-5/Abel-Osaretin/expense-tracker/src/menu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum Menu {
Add, View, Update, Remove, Quit,
}

impl Menu {
pub fn menu_select(selection: &str) -> Option<Menu> {
let selection = selection.trim().to_lowercase();
match selection.as_str() {
"add" | "1" => Some(Menu::Add),
"view" | "2" => Some(Menu::View),
"update" | "3" => Some(Menu::Update),
"remove" | "4" => Some(Menu::Remove),
"quit" | "q" => Some(Menu::Quit),
_ => None,
}
}
}
Loading