From b1b115b45c132fb75831a5d562a2aa0f5f8e3147 Mon Sep 17 00:00:00 2001 From: Musselman Date: Tue, 27 Aug 2024 10:43:21 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Feature:=20Add=20Transaction=20Func?= =?UTF-8?q?tions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit You can now deposit, withdraw, and send money --- Cargo.lock | 7 ++++++ src/main.rs | 65 ++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..238aef9 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "accounting-with-fundamental-rust-1" +version = "0.1.0" diff --git a/src/main.rs b/src/main.rs index 28e0e06..646d657 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,10 @@ use std::collections::HashMap; - /// An application-specific error type #[derive(Debug)] enum AccountingError { // Add variants here for account not found, account underfunded and account overfunded - AccountNotFound(String) , + AccountNotFound(String), AccountUnderFunded(String, u64), AccountOverFunded(String, u64), } @@ -15,8 +14,8 @@ enum AccountingError { #[derive(Debug)] pub enum Tx { // Add variants for storing withdraw/deposit transactions - Withdraw{account:String, amount:u64}, - Deposit{account:String, amount:u64}, + Withdraw { account: String, amount: u64 }, + Deposit { account: String, amount: u64 }, } /// A type for managing accounts and their current currency balance @@ -27,11 +26,10 @@ struct Accounts { } impl Accounts { - /// Returns an empty instance of the [`Accounts`] type pub fn new() -> Self { Accounts { - accounts: Default::default() + accounts: HashMap::new(), } } @@ -41,11 +39,13 @@ impl Accounts { pub fn deposit(&mut self, signer: &str, amount: u64) -> Result { if let Some(account) = self.accounts.get_mut(signer) { (*account) + // check the ammount to add and then add it .checked_add(amount) .and_then(|r| { *account = r; Some(r) }) + // if ok do nothing or if not ok throw error .ok_or(AccountingError::AccountOverFunded( signer.to_string(), amount, @@ -64,12 +64,32 @@ impl Accounts { } } - /// Withdraws the `amount` from the `signer` account. - /// # Errors - /// Attempted overflow - pub fn withdraw(&mut self, signer: &str, amount: u64) -> Result { - todo!(); - } + /// Withdraws the `amount` from the `signer` account. + /// # Errors + /// Attempted overflow + pub fn withdraw(&mut self, signer: &str, amount: u64) -> Result { + //check if account exists + if let Some(account) = self.accounts.get_mut(signer) { + (*account) + .checked_sub(amount) + .and_then(|r| { + *account = r; + Some(r) + }) + .ok_or(AccountingError::AccountUnderFunded( + signer.to_string(), + amount, + )) + .map(|_| Tx::Withdraw { + account: signer.to_string(), + amount, + }) + } else { + //if acount does not exist + Err(AccountingError::AccountNotFound(signer.to_string())) + } + } + /// Withdraws the amount from the sender account and deposits it in the recipient account. /// @@ -81,7 +101,26 @@ impl Accounts { recipient: &str, amount: u64, ) -> Result<(Tx, Tx), AccountingError> { - todo!(); + match self.accounts.get(sender) { + Some(amt) if self.accounts.contains_key(recipient) && *amt >= amount => { + // The ? operator is a built-in shorthand for + // if let Err(e) = my_func_call() { return Err(e); } + let tx_withdraw = self.withdraw(sender, amount)?; + self.deposit(recipient, amount) + .map_err(|e| { + // return the funds to the sender on error + self.deposit(sender, amount).unwrap(); + e + }) + .map(|tx_deposit| (tx_withdraw, tx_deposit)) + } + Some(amt) if self.accounts.contains_key(recipient) && *amt < amount => Err( + AccountingError::AccountUnderFunded(sender.to_owned(), amount), + ), + // The matching rules are evaluated from top to bottom and since all other cases where covered before, this rule means that the recipient's account wasn't found + Some(_) => Err(AccountingError::AccountNotFound(recipient.to_owned())), + None => Err(AccountingError::AccountNotFound(sender.to_string())), + } } }