use rust_decimal::prelude::ToPrimitive;
use std::{
fmt::{Debug, Display},
iter::Sum,
marker::PhantomData,
str::FromStr,
};
use crate::{
BaseMoney, BaseOps, Decimal, MoneyError, MoneyOps,
base::{Amount, MoneyParser},
macros::dec,
};
use crate::{Currency, MoneyFormatter};
use rust_decimal::MathematicalOps;
#[derive(Copy, PartialEq, Eq)]
pub struct Money<C: Currency> {
amount: Decimal,
_currency: PhantomData<C>,
}
impl<C: Currency> Default for Money<C> {
fn default() -> Self {
Self {
amount: Decimal::default(),
_currency: PhantomData,
}
}
}
impl<C: Currency> Ord for Money<C>
where
C: Currency + PartialEq + Eq,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.amount.cmp(&other.amount)
}
}
impl<C> PartialOrd for Money<C>
where
C: Currency + PartialEq + Eq,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<C> Amount<C> for Money<C>
where
C: Currency,
{
#[inline(always)]
fn get_decimal(&self) -> Option<Decimal> {
Some(self.amount())
}
}
impl<C> FromStr for Money<C>
where
C: Currency,
{
type Err = MoneyError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
let dec_num = Decimal::from_str(s).map_err(|err| {
MoneyError::ParseStrError(format!("failed parsing money from string: {}", err).into())
})?;
Ok(Self::from_decimal(dec_num))
}
}
impl<C: Currency> Clone for Money<C> {
fn clone(&self) -> Self {
Self {
amount: self.amount,
_currency: PhantomData,
}
}
}
impl<C> Display for Money<C>
where
C: Currency,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.display())
}
}
impl<C> Debug for Money<C>
where
C: Currency,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Money({}, {})", C::CODE, self.amount)
}
}
impl<C: Currency> Sum for Money<C> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Money::default(), |acc, b| acc + b)
}
}
impl<'a, C: Currency> Sum<&'a Money<C>> for Money<C> {
fn sum<I: Iterator<Item = &'a Money<C>>>(iter: I) -> Self {
iter.fold(Money::default(), |acc, b| acc + b.clone())
}
}
impl<C> BaseMoney<C> for Money<C>
where
C: Currency,
{
#[inline(always)]
fn from_decimal(amount: Decimal) -> Self {
Self {
amount: amount.round_dp(C::MINOR_UNIT.into()),
_currency: PhantomData,
}
}
#[inline(always)]
fn amount(&self) -> Decimal {
self.amount
}
#[inline(always)]
fn minor_amount(&self) -> Option<i128> {
self.amount()
.checked_mul(dec!(10).checked_powu(self.minor_unit().into())?)?
.to_i128()
}
}
impl<C> BaseOps<C> for Money<C> where C: Currency {}
impl<C> MoneyParser<C> for Money<C> where C: Currency {}
impl<C> MoneyFormatter<C> for Money<C> where C: Currency {}
impl<C> MoneyOps<C> for Money<C> where C: Currency {}