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
7 changes: 7 additions & 0 deletions submissions/week-2/day-5/Stephen-Ngozi-Merkle-Tree/Cargo.lock

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

6 changes: 6 additions & 0 deletions submissions/week-2/day-5/Stephen-Ngozi-Merkle-Tree/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "Stephen-Ngozi-Merkle-Tree"
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cargo package names are typically lowercase and snake_case; using uppercase letters can trigger warnings and won’t be accepted on crates.io. Consider changing name to a lowercase identifier (hyphens are ok, but lowercase is preferred).

Suggested change
name = "Stephen-Ngozi-Merkle-Tree"
name = "stephen-ngozi-merkle-tree"

Copilot uses AI. Check for mistakes.
version = "0.1.0"
edition = "2024"
Comment on lines +1 to +4
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The submission is described as an Expense Tracker, but the crate/package name is "Stephen-Ngozi-Merkle-Tree", which is confusing and suggests a different project. Consider renaming the package (and folder) to match the task (e.g., expense_tracker) to avoid confusion when building/running multiple submissions.

Copilot uses AI. Check for mistakes.

[dependencies]
17 changes: 17 additions & 0 deletions submissions/week-2/day-5/Stephen-Ngozi-Merkle-Tree/src/expense.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@


#[derive(Debug, Clone)]
pub enum TransactionType {
Credit,
Debit,
}



#[derive(Debug, Clone)]
pub struct Expense {
pub id: u8,
pub name: String,
pub amount: f64,
pub tx_type: TransactionType,
}
141 changes: 141 additions & 0 deletions submissions/week-2/day-5/Stephen-Ngozi-Merkle-Tree/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
mod expense;
mod tracker;

use std::io;
use expense::TransactionType;
use tracker::ExpenseTracker;




fn print_menu() {
println!("\n=== Expense Tracker ===");
println!("1 - Add Expense");
println!("2 - View Expenses");
println!("3 - Update Expense");
println!("4 - Delete Expense");
println!("q - Quit");
}
fn main() {
// println!("Hello, world!");

let mut tracker = ExpenseTracker::new();

loop {
print_menu();

let mut input = String::new();
io::stdin().read_line(&mut input).expect("Failed");
let input = input.trim();


if input == "q" {
println!("Are you sure you want to quit? (y/n)");
let mut confirm = String::new();
io::stdin().read_line(&mut confirm).expect("Failed ");
let confirm = confirm.trim();

if confirm == "y" {
tracker.save_to_file("stephen_expences.txt");
println!("Data saved to stephen_expences.txt");
Comment on lines +39 to +40
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filename "stephen_expences.txt" contains a spelling error ("expences"). Consider renaming to "stephen_expenses.txt" (and updating the checked-in file name) to avoid confusion.

Suggested change
tracker.save_to_file("stephen_expences.txt");
println!("Data saved to stephen_expences.txt");
tracker.save_to_file("stephen_expenses.txt");
println!("Data saved to stephen_expenses.txt");

Copilot uses AI. Check for mistakes.
println!("Bye bye go home!");
break;
}
}else if input == "1" {
println!("Enter expense name:");
let mut name = String::new();
io::stdin().read_line(&mut name).expect("Failed to read input");
let name = name.trim().to_string();

println!("Enter amount:");
let mut amount_input = String::new();
io::stdin().read_line(&mut amount_input).expect("Failed to read input");
let amount: f64 = amount_input.trim().parse().expect("Please enter a valid number");

println!("Is this Credit or Debit? (c/d):");
let mut tx_input = String::new();
io::stdin().read_line(&mut tx_input).expect("Failed to read input");
let tx_input = tx_input.trim();

let tx_type = if tx_input == "c" {
TransactionType::Credit
} else {
TransactionType::Debit
Comment on lines +55 to +63
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any transaction type input other than "c" is treated as Debit, so typos like "C"/"credit"/"x" will silently become Debit. Validate the input (case-insensitive "c" or "d") and re-prompt or show an error instead of defaulting.

Suggested change
println!("Is this Credit or Debit? (c/d):");
let mut tx_input = String::new();
io::stdin().read_line(&mut tx_input).expect("Failed to read input");
let tx_input = tx_input.trim();
let tx_type = if tx_input == "c" {
TransactionType::Credit
} else {
TransactionType::Debit
let tx_type = loop {
println!("Is this Credit or Debit? (c/d):");
let mut tx_input = String::new();
io::stdin().read_line(&mut tx_input).expect("Failed to read input");
let tx_input = tx_input.trim().to_lowercase();
match tx_input.as_str() {
"c" => break TransactionType::Credit,
"d" => break TransactionType::Debit,
_ => {
println!("Invalid input. Please enter 'c' for Credit or 'd' for Debit.");
}
}

Copilot uses AI. Check for mistakes.
};

let new_expense = tracker.add(name, amount, tx_type);
println!(" Expense added successfully!");
println!("ID: {}, Name: {}, Amount: ${:.2}, Type: {:?}",
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currency formatting is inconsistent: the add flow prints "$" while the view/update flows print "Naria...". Pick a single currency label/symbol and use it consistently across all outputs.

Suggested change
println!("ID: {}, Name: {}, Amount: ${:.2}, Type: {:?}",
println!("ID: {}, Name: {}, Amount: Naria{:.2}, Type: {:?}",

Copilot uses AI. Check for mistakes.
new_expense.id, new_expense.name, new_expense.amount, new_expense.tx_type);
}else if input == "2" {
let expenses = tracker.view_all();

if expenses.is_empty() {
println!("No Expenses in the Tracker");
} else {
println!("All Expenses");
for expense in expenses {
println!("ID: {} | Name: {} | Amount: Naria{:.2} | Type: {:?}",
expense.id, expense.name, expense.amount, expense.tx_type);
}
Comment on lines +78 to +80
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Naria" appears to be a misspelling of the currency name "Naira". Fixing the string will make output clearer.

Copilot uses AI. Check for mistakes.
}
} else if input == "3" {
println!("Enter the ID of the Expence");

Comment on lines +83 to +84
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prompt text uses "Expence" instead of "Expense".

Copilot uses AI. Check for mistakes.
let mut new_id_input = String::new();

io::stdin().read_line(&mut new_id_input).expect("Failed to read input");
let id: u8 = new_id_input.trim().parse().expect("Please enter a valid ID");



println!("Enter new amount:");
let mut amount_input = String::new();
io::stdin().read_line(&mut amount_input).expect("Failed to read input");
let amount: f64 = amount_input.trim().parse().expect("Please enter a valid number");

println!("Is this Credit or Debit? (c/d):");
let mut tx_input = String::new();
io::stdin().read_line(&mut tx_input).expect("Failed to read input");
let tx_input = tx_input.trim();

let tx_type = if tx_input == "c" {
TransactionType::Credit
} else {
TransactionType::Debit
Comment on lines +98 to +105
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the update flow, any transaction type input other than "c" becomes Debit, which can record the wrong transaction type. Add validation/re-prompt for "c" or "d" (case-insensitive).

Suggested change
let mut tx_input = String::new();
io::stdin().read_line(&mut tx_input).expect("Failed to read input");
let tx_input = tx_input.trim();
let tx_type = if tx_input == "c" {
TransactionType::Credit
} else {
TransactionType::Debit
let tx_type = loop {
let mut tx_input = String::new();
io::stdin().read_line(&mut tx_input).expect("Failed to read input");
let tx_input = tx_input.trim().to_lowercase();
match tx_input.as_str() {
"c" => break TransactionType::Credit,
"d" => break TransactionType::Debit,
_ => {
println!("Invalid transaction type. Please enter 'c' for Credit or 'd' for Debit:");
}
}

Copilot uses AI. Check for mistakes.
};

if tracker.update(id, amount, tx_type){
println!("Expense Updated Successfu");
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Status message "Expense Updated Successfu" is truncated/misspelled; correct it to a complete word to avoid confusing output.

Suggested change
println!("Expense Updated Successfu");
println!("Expense Updated Successfully");

Copilot uses AI. Check for mistakes.

if let Some(expense) = tracker.values.get(&id) {
println!("ID: {}, Name: {}, Amount: Naria{:.2}, Type: {:?}",
Comment on lines +108 to +112
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If tracker.update(...) returns false (ID not found), the program prints nothing and returns to the menu. Add an else branch to inform the user that the ID was not found.

Copilot uses AI. Check for mistakes.
expense.id, expense.name, expense.amount, expense.tx_type);
}else {
Comment on lines +112 to +114
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Naria" appears to be a misspelling of "Naira" here as well.

Copilot uses AI. Check for mistakes.
println!("Expense with ID {} not found!", id);

}
}
}else if input == "4" {
println!("Enter the ID of the expense you to delete:");
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prompt text is grammatically incorrect: "Enter the ID of the expense you to delete:". Update it to something like "Enter the ID of the expense you want to delete:" for clarity.

Suggested change
println!("Enter the ID of the expense you to delete:");
println!("Enter the ID of the expense you want to delete:");

Copilot uses AI. Check for mistakes.

let mut id_input = String::new();

io::stdin().read_line(&mut id_input).expect("Failed to read input");
let id: u8 = id_input.trim().parse().expect("Please enter a valid ID");

if tracker.delete(id){
println!(" Your Expence is Deleted ");
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete confirmation message uses "Expence" instead of "Expense".

Suggested change
println!(" Your Expence is Deleted ");
println!(" Your Expense is Deleted ");

Copilot uses AI. Check for mistakes.
}else {
println!("Expense with ID {} not found!", id);
}
}else {
println!("Invalid choice. Please try again.");
}




}

}
84 changes: 84 additions & 0 deletions submissions/week-2/day-5/Stephen-Ngozi-Merkle-Tree/src/tracker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use std::{collections::HashMap,fs::File,io::Write};
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import list is missing standard spacing after commas (HashMap,fs::File,io::Write), which will fail rustfmt/linting if enabled. Run cargo fmt or fix spacing.

Suggested change
use std::{collections::HashMap,fs::File,io::Write};
use std::{collections::HashMap, fs::File, io::Write};

Copilot uses AI. Check for mistakes.
use crate::expense::{Expense, TransactionType};


// Expense tracker
// Add the expenses
// Remove
// Update
// View

// Hashmaps
// structs
// enums
// Hashmaps




pub struct ExpenseTracker {
pub values: HashMap<u8, Expense>,
pub next_id: u8,
}
Comment on lines +19 to +22
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using u8 for IDs means next_id will overflow after 255 expenses (panic in debug builds, wrap in release), which can lead to duplicate IDs and lost data. Use a larger type (e.g., u32/u64/usize) and/or handle overflow explicitly.

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +22
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

values and next_id are public, which exposes internal state (and allows callers to mutate/replace entries without invariants). Prefer keeping fields private and providing methods like get(id)/iter() for read access.

Copilot uses AI. Check for mistakes.

impl ExpenseTracker {
pub fn new() -> Self {
Self {
values: HashMap::new(),
next_id: 1,
}
}

pub fn add(&mut self, name: String, amount: f64, tx_type: TransactionType) -> Expense {
let current_id = self.next_id;
let new_expense = Expense {
id: current_id,
name,
amount,
tx_type,
};
self.values.insert(current_id, new_expense.clone());
self.next_id += 1;
new_expense
}

pub fn view_all(&self) -> Vec<&Expense> {
let mut expenses: Vec<&Expense> = self.values.values().collect();

expenses.sort_by(|a, b|{a.id.cmp(&b.id)});
expenses

}

pub fn update(&mut self, id: u8, amount: f64, tx_type: TransactionType) -> bool {
match self.values.get_mut(&id) {
Some(exp) => {
exp.amount = amount;
exp.tx_type = tx_type;
true
}
None => false,
}
// let updated_expense = Expense {
// id,
// amount,
// tx_type,
// };
// self.values.put(id)
Comment on lines +62 to +67
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a block of commented-out, unfinished code inside update. Removing dead/commented code will make the method easier to read and maintain.

Suggested change
// let updated_expense = Expense {
// id,
// amount,
// tx_type,
// };
// self.values.put(id)

Copilot uses AI. Check for mistakes.
}

pub fn delete(&mut self, id: u8) -> bool {
self.values.remove(&id).is_some()
}


pub fn save_to_file(&self, filename: &str) {
let mut file = File::create(filename).expect("Could not create file");

for expense in self.values.values() {
let line = format!("ID: {}, Name: {}, Amount: {:.2}, Type: {:?}\n",
expense.id, expense.name, expense.amount, expense.tx_type);
file.write_all(line.as_bytes()).expect("Could not write to file");
}
Comment on lines +78 to +82
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

save_to_file writes expenses in HashMap iteration order, which is non-deterministic; this can produce shuffled output across runs. Consider iterating over view_all() (sorted) before writing to make saved files stable/readable.

Copilot uses AI. Check for mistakes.
Comment on lines +75 to +82
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

save_to_file panics on I/O errors via expect(...). Since this is a public method, returning std::io::Result<()> would let the caller decide how to handle failures (retry, show message, etc.).

Suggested change
pub fn save_to_file(&self, filename: &str) {
let mut file = File::create(filename).expect("Could not create file");
for expense in self.values.values() {
let line = format!("ID: {}, Name: {}, Amount: {:.2}, Type: {:?}\n",
expense.id, expense.name, expense.amount, expense.tx_type);
file.write_all(line.as_bytes()).expect("Could not write to file");
}
pub fn save_to_file(&self, filename: &str) -> std::io::Result<()> {
let mut file = File::create(filename)?;
for expense in self.values.values() {
let line = format!("ID: {}, Name: {}, Amount: {:.2}, Type: {:?}\n",
expense.id, expense.name, expense.amount, expense.tx_type);
file.write_all(line.as_bytes())?;
}
Ok(())

Copilot uses AI. Check for mistakes.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ID: 2, Name: Beans, Amount: 5000.00, Type: Credit
ID: 1, Name: Rice, Amount: 20000.00, Type: Credit
Loading