142 lines
2.9 KiB
Rust
142 lines
2.9 KiB
Rust
|
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<H: Hasher>(&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());
|
||
|
}
|
||
|
}
|