Feature: Add Transaction Functions

You can now deposit, withdraw, and send money
This commit is contained in:
James Musselman 2024-08-27 10:43:21 -05:00
parent 581c06fc2f
commit b1b115b45c
No known key found for this signature in database
GPG key ID: 1DAEFF35ECB5D6DB
2 changed files with 59 additions and 13 deletions

7
Cargo.lock generated Normal file
View file

@ -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"

View file

@ -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<Tx, AccountingError> {
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<Tx, AccountingError> {
todo!();
}
/// Withdraws the `amount` from the `signer` account.
/// # Errors
/// Attempted overflow
pub fn withdraw(&mut self, signer: &str, amount: u64) -> Result<Tx, AccountingError> {
//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())),
}
}
}