diff --git a/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/.gitignore b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/.gitignore new file mode 100644 index 0000000..c41cc9e --- /dev/null +++ b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/.gitignore @@ -0,0 +1 @@ +/target \ No newline at end of file diff --git a/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/Cargo.lock b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/Cargo.lock new file mode 100644 index 0000000..76d54ae --- /dev/null +++ b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/Cargo.lock @@ -0,0 +1,107 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "expense-tracker" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "zmij" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445" diff --git a/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/Cargo.toml b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/Cargo.toml new file mode 100644 index 0000000..53fd0b7 --- /dev/null +++ b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/Cargo.toml @@ -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" \ No newline at end of file diff --git a/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/README.md b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/README.md new file mode 100644 index 0000000..9fa1a97 --- /dev/null +++ b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/README.md @@ -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`. diff --git a/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/expenses.json b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/expenses.json new file mode 100644 index 0000000..b67053a --- /dev/null +++ b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/expenses.json @@ -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" + } +} \ No newline at end of file diff --git a/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/src/main.rs b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/src/main.rs new file mode 100644 index 0000000..0dbcce0 --- /dev/null +++ b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/src/main.rs @@ -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"); + } + } +} diff --git a/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/src/menu.rs b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/src/menu.rs new file mode 100644 index 0000000..b3f3aab --- /dev/null +++ b/submissions/week-2/day-5/Abel-Osaretin/expense-tracker/src/menu.rs @@ -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