use std::hash::{Hash, Hasher}; use thiserror::Error; use anyhow::{bail, Result}; use uuid::Uuid; use crate::account::AccountError::{Inactive, InvalidAmount, Overdraw}; #[derive(Error, Debug)] pub enum AccountError { #[error("can not overdraw account")] Overdraw(), #[error("account is inactive")] Inactive(), #[error("amount must be > 0")] InvalidAmount(), } #[derive(Debug, Clone)] pub struct Account { pub number: String, pub owner: String, pub balance: f64, pub is_active: bool, } impl Default for Account { fn default() -> Self { Account { number: Uuid::new_v4().to_string(), owner: "".into(), balance: 0_f64, is_active: true, } } } impl PartialEq for Account { fn eq(&self, other: &Self) -> bool { self.number == other.number } } impl Eq for Account {} impl Hash for Account { fn hash(&self, state: &mut H) { self.number.hash(state); } } impl Account { #[cfg(test)] pub fn new() -> Self { Self { ..Default::default() } } pub fn deposit(&mut self, amount: f64) -> Result<()> { self.check_account(amount)?; self.balance += amount; Ok(()) } pub fn withdraw(&mut self, amount: f64) -> Result<()> { self.check_account(amount)?; if self.balance - amount < 0 as f64 { bail!(Overdraw()); } self.balance -= amount; Ok(()) } fn check_account(&self, amount: f64) -> Result<()> { if !self.is_active { bail!(Inactive()); } if amount < 0 as f64 { bail!(InvalidAmount()); } Ok(()) } pub fn passivate(&mut self) -> bool { let is_passivated = self.balance <= 0 as f64 && self.is_active; if is_passivated { self.is_active = false; } is_passivated } } #[cfg(test)] mod tests { use super::*; #[test] fn deposits() { let mut acc = Account::new(); let ok_result = acc.deposit(10.56); assert!(ok_result.is_ok()); let err_result = acc.deposit(-5.89); assert!(err_result.is_err()); } #[test] fn withdrawals() { let mut acc = Account::new(); let ok_result1 = acc.deposit(10_f64); assert!(ok_result1.is_ok()); let ok_result2 = acc.withdraw(5_f64); assert!(ok_result2.is_ok()); let err_result1 = acc.withdraw(10_f64); assert!(err_result1.is_err()); let err_result2 = acc.withdraw(-10_f64); assert!(err_result2.is_err()); } #[test] fn passivation() { let mut acc = Account::new(); let deposit_amount = 100_f64; acc.deposit(deposit_amount).unwrap(); assert!(!acc.passivate()); acc.withdraw(deposit_amount).unwrap(); assert!(acc.passivate()); assert!(!acc.passivate()); } }