use crate::common::tmf_error::TMFError;
use rust_decimal::{prelude::FromPrimitive, Decimal};
use serde::{Deserialize, Serialize};
use std::ops::{Add, AddAssign, Div, Mul, Sub};
const MONEY_DEFAULT_UNIT: &str = "AUD";
#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize)]
pub struct Money {
pub unit: String,
pub value: Decimal,
}
impl Money {
#[cfg(not(target_arch = "wasm32"))]
pub fn currency(&mut self, currency_code: &str) -> Result<String, TMFError> {
let c = rust_iso4217::from_code(currency_code);
match c {
Some(c) => {
self.unit = c.code.into();
Ok(self.unit.clone())
}
None => Err(TMFError::CurrencyError(format!(
"Currency Code not found: {currency_code}"
))),
}
}
#[cfg(target_arch = "wasm32")]
pub fn currency(&mut self, currency_code: &str) -> Result<String, TMFError> {
let c = rust_iso4217::from_code(currency_code);
match c {
Some(c) => {
self.unit = c.code();
Ok(self.unit.clone())
}
None => Err(TMFError::CurrencyError(format!(
"Currency Code not found: {}",
currency_code
))),
}
}
}
impl From<i32> for Money {
fn from(value: i32) -> Self {
Money {
value: Decimal::from(value),
unit: MONEY_DEFAULT_UNIT.to_string(),
}
}
}
impl From<f32> for Money {
fn from(value: f32) -> Self {
Money {
value: Decimal::from_f32(value).unwrap_or_default(),
unit: MONEY_DEFAULT_UNIT.to_string(),
}
}
}
impl Add for Money {
type Output = Money;
fn add(self, rhs: Self) -> Self::Output {
if self.unit == rhs.unit {
Self {
unit: self.unit.clone(),
value: self.value + rhs.value,
}
} else {
self
}
}
}
impl AddAssign for Money {
fn add_assign(&mut self, rhs: Self) {
self.value += rhs.value;
}
}
impl Sub for Money {
type Output = Money;
fn sub(self, rhs: Self) -> Self::Output {
if self.unit == rhs.unit {
Self {
unit: self.unit.clone(),
value: self.value - rhs.value,
}
} else {
self
}
}
}
impl Mul for Money {
type Output = Money;
fn mul(self, rhs: Self) -> Self::Output {
if self.unit == rhs.unit {
Self {
unit: self.unit.clone(),
value: self.value * rhs.value,
}
} else {
self
}
}
}
impl Mul<f32> for Money {
type Output = Money;
fn mul(self, rhs: f32) -> Self::Output {
Self {
unit: self.unit.clone(),
value: self.value * Decimal::from_f32(rhs).unwrap_or_default(),
}
}
}
impl Mul<u32> for Money {
type Output = Money;
fn mul(self, rhs: u32) -> Self::Output {
Self {
unit: self.unit.clone(),
value: self.value * Decimal::from(rhs),
}
}
}
impl Div for Money {
type Output = Money;
fn div(self, rhs: Self) -> Self::Output {
if self.unit == rhs.unit && rhs.value != Decimal::ZERO {
Self {
unit: self.unit.clone(),
value: self.value / rhs.value,
}
} else {
self
}
}
}
impl Div<f32> for Money {
type Output = Money;
fn div(self, rhs: f32) -> Self::Output {
let dec_val = Decimal::from_f32(rhs).unwrap_or_default();
if dec_val != Decimal::ZERO {
Self {
unit: self.unit.clone(),
value: self.value / dec_val,
}
} else {
self
}
}
}
impl Div<i32> for Money {
type Output = Money;
fn div(self, rhs: i32) -> Self::Output {
let dec_val = Decimal::from(rhs);
if dec_val != Decimal::ZERO {
Self {
unit: self.unit.clone(),
value: self.value / dec_val,
}
} else {
self
}
}
}
#[cfg(test)]
mod test {
use rust_decimal::prelude::FromPrimitive;
use super::*;
const MONEY_JSON: &str = "{
\"unit\" : \"AUD\",
\"value\" : 12.34
}";
#[test]
fn test_valid_currency() {
let mut money = Money::default();
let result = money.currency("AUD");
assert_eq!(result.is_ok(), true);
assert_eq!(money.unit, "AUD".to_string());
}
#[test]
fn test_invalid_currency() {
let mut money = Money::default();
let result = money.currency("INVALID");
assert_eq!(result.is_err(), true);
}
#[test]
fn test_money_deserialize() {
let money: Money = serde_json::from_str(MONEY_JSON).expect("MONEY_JSON");
assert_eq!(money.unit.as_str(), "AUD");
assert_eq!(money.value, Decimal::from_f32(12.34).unwrap_or_default());
}
#[test]
fn test_money_add() {
let money1 = Money::from(10);
let money2 = Money::from(37);
let money_sum = money1 + money2;
assert_eq!(money_sum.value, Money::from(47).value);
}
#[test]
fn test_money_sub() {
let money1 = Money::from(37);
let money2 = Money::from(10);
let money_sub = money1 - money2;
assert_eq!(money_sub.value, Money::from(27).value);
}
#[test]
fn test_money_mul() {
let money1 = Money::from(16);
let money2 = Money::from(3);
let money_mul = money1 * money2;
assert_eq!(money_mul.value, Money::from(48).value);
}
#[test]
fn test_money_mul_f32() {
let money1 = Money::from(16);
let money_mul = money1 * 3.0;
assert_eq!(money_mul.value, Money::from(48).value);
}
#[test]
fn test_money_mul_i32() {
let money1 = Money::from(16);
let money_mul = money1 * 3;
assert_eq!(money_mul.value, Money::from(48).value);
}
#[test]
fn test_money_div_f32() {
let money1 = Money::from(48);
let money_div = money1 / 3.0;
assert_eq!(money_div.value, Money::from(16).value);
}
#[test]
fn test_money_div_i32() {
let money1 = Money::from(48);
let money_div = money1 / 3;
assert_eq!(money_div.value, Money::from(16).value);
}
}