diff --git a/live-session/week-1 & 2/day-10/expense-tracker/src/main.rs b/live-session/week-1 & 2/day-10/expense-tracker/src/main.rs index a544e80..ff1309f 100644 --- a/live-session/week-1 & 2/day-10/expense-tracker/src/main.rs +++ b/live-session/week-1 & 2/day-10/expense-tracker/src/main.rs @@ -63,12 +63,6 @@ impl ExpenseTracker { } None => false, } - // let updated_expense = Expense { - // id, - // amount, - // tx_type, - // }; - // self.values.put(id) } fn delete(&mut self, id: u8) -> bool { diff --git a/submissions/week-2/day-5/Kingsley-Expense-Tracker/.gitignore b/submissions/week-2/day-5/Kingsley-Expense-Tracker/.gitignore new file mode 100644 index 0000000..f962ec0 --- /dev/null +++ b/submissions/week-2/day-5/Kingsley-Expense-Tracker/.gitignore @@ -0,0 +1,2 @@ +/target +/logs.txt diff --git a/submissions/week-2/day-5/Kingsley-Expense-Tracker/Cargo.lock b/submissions/week-2/day-5/Kingsley-Expense-Tracker/Cargo.lock new file mode 100644 index 0000000..7f91d07 --- /dev/null +++ b/submissions/week-2/day-5/Kingsley-Expense-Tracker/Cargo.lock @@ -0,0 +1,284 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "cc" +version = "1.2.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "expense-tracker" +version = "0.1.0" +dependencies = [ + "chrono", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[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 = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[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 = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] diff --git a/submissions/week-2/day-5/Kingsley-Expense-Tracker/Cargo.toml b/submissions/week-2/day-5/Kingsley-Expense-Tracker/Cargo.toml new file mode 100644 index 0000000..a2f4731 --- /dev/null +++ b/submissions/week-2/day-5/Kingsley-Expense-Tracker/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "expense-tracker" +version = "0.1.0" +edition = "2024" + +[dependencies] +chrono = "0.4.43" diff --git a/submissions/week-2/day-5/Kingsley-Expense-Tracker/module.txt b/submissions/week-2/day-5/Kingsley-Expense-Tracker/module.txt new file mode 100644 index 0000000..1a3e120 --- /dev/null +++ b/submissions/week-2/day-5/Kingsley-Expense-Tracker/module.txt @@ -0,0 +1,107 @@ +The Rust module system - this is fairly ignored when learning a language cause its not part of the cool stuffs, +and sadly one thing I never cared to understand in Javascript, I always go the easy route, setup a ts config file for absolute paths, and allow my IDEs do the magic. +The annoying part was the long chain of dot slashes(though absolute paths takes care of that). +With Rust though confusing but very elegant once you master it.There are three major keywords + +pub - what it is… +mod - what it is.. +use - what it is.. + +In rust there are two ways to mark a function, variable or block as a module and that is using the mod keyword, how to use + +Just add + + +mod expenses { + struct Expense { + title: String, + amount: f64, + date: String, + } + + + impl Expense { + fn new(title: String, amount: f64, date: String) -> Self { + Expense { title, amount, date } + } + } +} + + +fn main() { + let expense = expenses::Expense::new( + String::from("Groceries"), + 150.0, + String::from("2024-06-01"), + ); + + println!("Expense: {} - ${} on {}", expense.title, expense.amount, expense.date); +} + + +If we notice we are using the struct Expense from the expenses module, +but we are not able to access it because its private by default, +to make it public we need to add the pub keyword before the struct definition and also before the new function, like this. + + +Explain the keyword pub: pub is a keyword in Rust that is used to make items (such as functions, structs, enums, etc.) +public and accessible from outside the module they are defined in. +By default, items in Rust are private, meaning they can only be accessed within the same module. +When you use the pub keyword, you are explicitly allowing other modules to access and use those items. + + + +mod expenses { + pub struct Expense { + title: String, + amount: f64, + date: String, + } + + + impl Expense { + pub fn new(title: String, amount: f64, date: String) -> Self { + Expense { title, amount, date } + } + } +} + + +fn main() { + let expense = expenses::Expense::new( + String::from("Groceries"), + 150.0, + String::from("2024-06-01"), + ); + + println!("Expense: {} - ${} on {}", expense.title, expense.amount, expense.date); +} + + +We have a problem, when we run this code, we will get an error because the fields of the struct Expense are still private, to make them public we need to add the pub keyword before each field, like this. + +mod expenses { + pub struct Expense { + pub title: String, + pub amount: f64, + pub date: String, + } + + impl Expense { + pub fn new(title: String, amount: f64, date: String) -> Self { + Expense { title, amount, date } + } + } +} + + +fn main() { + let expense = expenses::Expense::new( + String::from("Groceries"), + 150.0, + String::from("2024-06-01"), + ); + + println!("Expense: {} - ${} on {}", expense.title, expense.amount, expense.date); +} + diff --git a/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/logger_module/logger.rs b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/logger_module/logger.rs new file mode 100644 index 0000000..5580287 --- /dev/null +++ b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/logger_module/logger.rs @@ -0,0 +1,18 @@ +use chrono::Local; +use std::io::Write; +use std::{fs::OpenOptions, io::Result}; + +pub fn log(operation: &str) -> Result<()> { + let mut file = OpenOptions::new() + .create(true) + .append(true) + .open("logs.txt")?; + + let timestamp = Local::now().format("%Y-%M-%D %H:%M:%S"); + + if let Err(e) = writeln!(file, "{timestamp} {operation}") { + println!("Logging to file failed: {e}") + } + + Ok(()) +} diff --git a/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/logger_module/mod.rs b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/logger_module/mod.rs new file mode 100644 index 0000000..cbcf7f6 --- /dev/null +++ b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/logger_module/mod.rs @@ -0,0 +1,3 @@ +mod logger; + +pub use logger::log; diff --git a/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/main.rs b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/main.rs new file mode 100644 index 0000000..f22f14a --- /dev/null +++ b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/main.rs @@ -0,0 +1,51 @@ +mod model; +mod logger_module; +mod ui; + +use model::{ExpenseTracker, TrackerActions}; +use logger_module as logger; +use std::io; +use ui::display_actions; +use ui::start; + +fn main() { + let mut tracker = ExpenseTracker::new(); + println!( + r#" + _____ + | ____|_ ___ __ ___ _ __ ___ ___ + | _| \ \/ / '_ \ / _ \ '_ \/ __|/ _ \ + | |___ > <| |_) | __/ | | \__ \ __/ + |_____/_/\_\ .__/ \___|_| |_|___/\___| + |_| + T R A C K E R + "# + ); + + println!( + r#" +Welcome to Expense Tracker v1.0! +--------------------------------- +Manage your income and expenses efficiently. +Usage: Select an option from the menu below to get started. +All data is saved automatically in memmory. +"# + ); + + loop { + display_actions(); + + let mut action = String::new(); + io::stdin() + .read_line(&mut action) + .expect("Please enter a valid character"); + + let action = TrackerActions::get_power_action(&action); + + if let Some(act) = action { + start(&act, &mut tracker) + } else { + println!("Failed to process command. Please enter a valid input") + } + } +} diff --git a/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/model/expense.rs b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/model/expense.rs new file mode 100644 index 0000000..ef136d0 --- /dev/null +++ b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/model/expense.rs @@ -0,0 +1,13 @@ +#[derive(Debug, Clone)] +pub enum TransactionType { + Credit, + Debit, +} + +#[derive(Debug, Clone)] +pub struct Expense { + pub id: u8, + pub title: String, + pub amount: u32, + pub tx_type: TransactionType, +} diff --git a/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/model/mod.rs b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/model/mod.rs new file mode 100644 index 0000000..1780cf5 --- /dev/null +++ b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/model/mod.rs @@ -0,0 +1,5 @@ +mod expense; +mod tracker; + +pub use expense::{Expense, TransactionType}; +pub use tracker::{ExpenseTracker, TrackerActions}; diff --git a/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/model/tracker.rs b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/model/tracker.rs new file mode 100644 index 0000000..fa2fb9a --- /dev/null +++ b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/model/tracker.rs @@ -0,0 +1,84 @@ +use super::expense::{Expense, TransactionType}; +use std::collections::HashMap; + +pub enum TrackerActions { + ViewAll, + Add, + Edit, + ViewSingle, + Delete, + Quit, +} + +impl TrackerActions { + pub fn get_power_action(action: &str) -> Option { + match action.trim().to_lowercase().as_str() { + "1" => Some(Self::ViewAll), + "2" => Some(Self::Add), + "3" => Some(Self::Edit), + "4" => Some(Self::ViewSingle), + "5" => Some(Self::Delete), + "q" => Some(Self::Quit), + _ => None, + } + } +} +pub struct ExpenseTracker { + pub values: HashMap, + pub next_key: u8, +} + +impl ExpenseTracker { + pub fn new() -> Self { + Self { + values: HashMap::new(), + next_key: 1, + } + } + + pub fn add(&mut self, expense: &Expense) -> Result { + let new_expense = Expense { + id: self.next_key, + title: expense.title.clone(), + amount: expense.amount, + tx_type: expense.tx_type.clone(), + }; + self.values.insert(self.next_key, new_expense.clone()); + self.next_key += 1; + + Ok(new_expense) + } + + pub fn get_all_expenses(&self) -> Vec<&Expense> { + self.values.values().collect() + } + + pub fn update_item( + &mut self, + id: u8, + amount: u32, + tx_type: TransactionType, + ) -> Result<&Expense, String> { + // Read more on the relationship between match. and use of ok, ok_or, ok_else + let item = self + .values + .get_mut(&id) + .ok_or_else(|| format!("Expense not found"))?; + + item.amount = amount; + item.tx_type = tx_type; + Ok(item) + } + + pub fn get_single_item(&self, id: u8) -> Option<&Expense> { + self.values.get(&id) + } + + pub fn delete_expense(&mut self, id: u8) -> Result { + if let Some(item) = self.values.remove(&id) { + Ok(item) + } else { + Err("An error occured deleting item") + } + } +} diff --git a/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/ui/mod.rs b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/ui/mod.rs new file mode 100644 index 0000000..97036e5 --- /dev/null +++ b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/ui/mod.rs @@ -0,0 +1,5 @@ +mod track_expense; +mod view; + +pub use track_expense::start; +pub use view::display_actions; diff --git a/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/ui/track_expense.rs b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/ui/track_expense.rs new file mode 100644 index 0000000..e343c29 --- /dev/null +++ b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/ui/track_expense.rs @@ -0,0 +1,228 @@ +use crate::model::{Expense, ExpenseTracker, TrackerActions, TransactionType}; +use crate::logger; +use std::io; + +// This does not have a return value because its just reponsible for io operations +pub fn start(action: &TrackerActions, tracker: &mut ExpenseTracker) { + match action { + TrackerActions::ViewAll => { + let len = tracker.get_all_expenses().len(); + + if len <= 0 { + println!("Expense list is empty!") + } else { + for expense in tracker.get_all_expenses() { + println!( + "Expense ID: {} -> Title: {} -> Amount: N{} -> Transaction Type: {:?}", + expense.id, expense.title, expense.amount, expense.tx_type + ) + } + } + + let _ = logger::log("All expense was viewed"); + } + TrackerActions::Add => { + let mut amount = String::new(); + let mut title = String::new(); + let mut tx_type = String::new(); + + println!("Enter Expense title: "); + + title.clear(); + io::stdin() + .read_line(&mut title) + .expect("Please enter a vlaid input"); + + let parsed_amount = loop { + println!("Enter Amount: "); + amount.clear(); + io::stdin() + .read_line(&mut amount) + .expect("Please enter a vlaid amount field, it should be an integer"); + + match amount.trim().parse::() { + Ok(amt) => break amt, + Err(_) => println!("❌ Invalid entry. Please enter a positive number ‼️ "), + } + }; + + let parsed_type = loop { + println!("Enter Transaction type (debit/credit): "); + tx_type.clear(); + io::stdin() + .read_line(&mut tx_type) + .expect("⚠️ Please enter a valid transaction type. (debit/credit)"); + match tx_type.trim().to_lowercase().as_str() { + "credit" => break TransactionType::Credit, + "debit" => break TransactionType::Debit, + _ => println!("❌ Invalid transaction type!! Please try again"), + }; + }; + + let new_expense = Expense { + id: tracker.next_key, + title: title.trim().to_string(), + amount: parsed_amount, + tx_type: parsed_type, + }; + + if let Ok(added_expense) = tracker.add(&new_expense) { + let added_expense = format!( + "Added {} to the list with amount N{}", + added_expense.title, added_expense.amount + ); + println!("{added_expense}"); + let _ = logger::log(added_expense.as_str()); + } else { + println!("❌ An error occured while adding expense to the list") + } + } + TrackerActions::Edit => { + let mut expense_id = String::new(); + let mut amount = String::new(); + let mut tx_type = String::new(); + + println!("Here is a list of expenses to modify\n"); + + for expense in tracker.get_all_expenses() { + println!( + "Expense ID: {} -> Title: {} - Amount: N{}", + expense.id, expense.title, expense.amount + ) + } + + let id = loop { + println!("\nEnter the id of the item you want to modify: "); + expense_id.clear(); + io::stdin() + .read_line(&mut expense_id) + .expect("Please enter a valid number"); + + match expense_id.trim().parse::() { + Ok(amt) => break amt, + Err(_) => println!("Invalid entry. Please enter a number"), + } + }; + + let parsed_amount = loop { + println!("Enter new amount: "); + amount.clear(); + io::stdin() + .read_line(&mut amount) + .expect("Please enter a vlaid amount field, it should be an integer"); + + match amount.trim().parse::() { + Ok(amt) => break amt, + Err(_) => println!("❌ Invalid entry ‼️. Please enter a number"), + } + }; + + let parsed_type = loop { + println!("Enter the new transaction type (debit/credit): "); + tx_type.clear(); + io::stdin() + .read_line(&mut tx_type) + .expect("Please enter a valid transaction type. (debit/credit)"); + match tx_type.trim().to_lowercase().as_str() { + "debit" => break TransactionType::Credit, + "credit" => break TransactionType::Debit, + _ => println!("Invalid transaction type!! Please try again"), + }; + }; + + if let Ok(item) = tracker.update_item(id, parsed_amount, parsed_type) { + let msg = format!( + "You changed expense ID {}'s amount N{} and transaction type {:?}", + item.id, item.amount, item.tx_type + ); + println!("{msg}"); + let _ = logger::log(&msg); + } else { + println!("❌ An error occured editing item") + } + } + TrackerActions::ViewSingle => { + println!( + "Here is a list of expenses, let me know the on you want to view more info about\n" + ); + + for expense in tracker.get_all_expenses() { + println!("Expense ID: {} -> Title: {}", expense.id, expense.title) + } + let mut expense_id = String::new(); + + let id = loop { + println!("⚠️ Enter the id of the item you want to view: "); + io::stdin() + .read_line(&mut expense_id) + .expect("Please enter a valid number"); + + match expense_id.trim().parse::() { + Ok(amt) => break amt, + Err(_) => println!("❌ Invalid entry. Please enter a number"), + } + }; + + if let Some(expense) = tracker.get_single_item(id) { + println!("Here you go!!\n"); + println!( + "Expense ID: {} -> Title: {} -> Amount: N{} -> Transaction Type: {:?}", + expense.id, expense.title, expense.amount, expense.tx_type + ); + + let msg = format!( + "Expense no {} with title: {} and amount: {} and type {:?} was viewed", + expense.id, expense.title, expense.amount, expense.tx_type + ); + + let _ = logger::log(&msg); + } else { + println!("❌ An Error Occured while viewing item") + } + } + TrackerActions::Delete => { + let mut expense_id = String::new(); + println!("Here is a list of expenses, let me know the on you want to delete\n"); + + for expense in tracker.get_all_expenses() { + println!("Expense ID: {} -> Title: {}", expense.id, expense.title) + } + + let id = loop { + println!("Enter the id of the item you want to delete: "); + + expense_id.clear(); + io::stdin() + .read_line(&mut expense_id) + .expect("Please enter a valid number"); + + match expense_id.trim().parse::() { + Ok(amt) => break amt, + Err(_) => println!("❌ Invalid entry. Please enter a number"), + } + }; + if let Ok(expense) = tracker.delete_expense(id) { + let msg = format!("Expense with ID: {} was deleted", expense.title); + println!("{msg}"); + let _ = logger::log(msg.as_str()); + } + } + + TrackerActions::Quit => loop { + println!("Are you sure you want to quit? (y/n)"); + let mut quit_command = String::new(); + quit_command.clear(); + io::stdin() + .read_line(&mut quit_command) + .expect("Please enter a valid string"); + + match quit_command.trim().to_lowercase().as_str() { + "y" => std::process::exit(1), + "n" => return, + _ => { + println!("⚠️ Invalid input please type a 'y' for yes or 'n' for no"); + } + } + }, + } +} diff --git a/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/ui/view.rs b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/ui/view.rs new file mode 100644 index 0000000..45dff85 --- /dev/null +++ b/submissions/week-2/day-5/Kingsley-Expense-Tracker/src/ui/view.rs @@ -0,0 +1,12 @@ +pub fn display_actions() { + println!("\n===================================="); + println!(" EXPENSE TRACKER MENU "); + println!("===================================="); + println!(" [1] View all expenses"); + println!(" [2] Add new expense"); + println!(" [3] Edit expense"); + println!(" [4] View single expense"); + println!(" [5] Delete expense"); + println!(" [q] Quit"); + println!("===================================="); +}