use super::CurrencyCode;
use std::{cmp, fmt, ops};
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Default, PartialEq, Eq)]
pub struct Amount {
#[serde(rename = "Value", default)]
pub value: i64,
#[serde(rename = "CurrencyCode")]
#[serde(default)]
pub currency_code: CurrencyCode,
}
impl Amount {
pub fn fractions(n: i64) -> Self {
Self {
value: n,
currency_code: CurrencyCode::Sek,
}
}
pub fn with_currency(&self, cc: CurrencyCode) -> Self {
Self {
value: self.value,
currency_code: cc,
}
}
pub fn short(&self) -> ShortAmount<'_> {
ShortAmount(self)
}
fn fmt_without_currency(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.value < 0 {
f.write_str("-")?;
}
match (self.value / 100).abs() {
n if n > 999_999_999_999_999_999 => write!(
f,
"{} {:03} {:03} {:03} {:03} {:03} {:03}",
n / 1_000_000_000_000_000_000,
(n / 1_000_000_000_000_000) % 1_000,
(n / 1_000_000_000_000) % 1_000,
(n / 1_000_000_000) % 1_000,
(n / 1_000_000) % 1_000,
(n / 1_000) % 1_000,
n % 1_000
),
n if n > 999_999_999_999_999 => write!(
f,
"{} {:03} {:03} {:03} {:03} {:03}",
n / 1_000_000_000_000_000,
(n / 1_000_000_000_000) % 1_000,
(n / 1_000_000_000) % 1_000,
(n / 1_000_000) % 1_000,
(n / 1_000) % 1_000,
n % 1_000
),
n if n > 999_999_999_999 => write!(
f,
"{} {:03} {:03} {:03} {:03}",
n / 1_000_000_000_000,
(n / 1_000_000_000) % 1_000,
(n / 1_000_000) % 1_000,
(n / 1_000) % 1_000,
n % 1_000
),
n if n > 999_999_999 => write!(
f,
"{} {:03} {:03} {:03}",
n / 1_000_000_000,
(n / 1_000_000) % 1_000,
(n / 1_000) % 1_000,
n % 1_000
),
n if n > 999_999 => write!(
f,
"{} {:03} {:03}",
n / 1_000_000,
(n / 1_000) % 1_000,
n % 1_000
),
n if n > 999 => write!(f, "{} {:03}", n / 1_000, n % 1_000,),
n => write!(f, "{n}"),
}?;
write!(f, ",{:02}", (self.value % 100).abs())
}
}
pub struct ShortAmount<'a>(&'a Amount);
impl fmt::Display for ShortAmount<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt_without_currency(f)
}
}
impl fmt::Debug for ShortAmount<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt_without_currency(f)
}
}
impl fmt::Display for Amount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.currency_code.as_str())?;
f.write_str(" ")?;
self.fmt_without_currency(f)
}
}
impl<T> ops::Add<T> for Amount
where
T: std::borrow::Borrow<Self>,
{
type Output = Self;
fn add(self, other: T) -> Self {
let other = other.borrow();
Self {
value: self.value + other.value,
currency_code: self.currency_code,
}
}
}
impl<T> ops::AddAssign<T> for Amount
where
T: std::borrow::Borrow<Self>,
{
fn add_assign(&mut self, other: T) {
self.value += other.borrow().value;
}
}
impl<T> ops::Sub<T> for Amount
where
T: std::borrow::Borrow<Self>,
{
type Output = Self;
fn sub(self, other: T) -> Self {
let other = other.borrow();
Self {
value: self.value - other.value,
currency_code: self.currency_code,
}
}
}
impl ops::Mul<i64> for Amount {
type Output = Self;
fn mul(self, other: i64) -> Self::Output {
Amount::fractions(self.value * other)
}
}
impl ops::Mul<i64> for &Amount {
type Output = Amount;
fn mul(self, other: i64) -> Self::Output {
Amount::fractions(self.value * other)
}
}
impl ops::Mul<i32> for Amount {
type Output = Self;
fn mul(self, other: i32) -> Self::Output {
Amount::fractions(self.value * other as i64)
}
}
impl ops::Mul<i32> for &Amount {
type Output = Amount;
fn mul(self, other: i32) -> Self::Output {
Amount::fractions(self.value * other as i64)
}
}
impl ops::Mul<u8> for Amount {
type Output = Self;
fn mul(self, other: u8) -> Self::Output {
Amount::fractions(self.value * other as i64)
}
}
impl ops::Mul<u8> for &Amount {
type Output = Amount;
fn mul(self, other: u8) -> Self::Output {
Amount::fractions(self.value * other as i64)
}
}
impl ops::Mul<f32> for Amount {
type Output = Self;
fn mul(self, other: f32) -> Self::Output {
let fac = (other * 100.).round() as i64;
Amount::fractions(self.value * fac / 100)
}
}
impl ops::Mul<f32> for &Amount {
type Output = Amount;
fn mul(self, other: f32) -> Self::Output {
let fac = (other * 100.).round() as i64;
Amount::fractions(self.value * fac / 100)
}
}
impl ops::Mul<f64> for Amount {
type Output = Self;
fn mul(self, other: f64) -> Self::Output {
let fac = (other * 100.).round() as i64;
Amount::fractions(self.value * fac / 100)
}
}
impl ops::Mul<f64> for &Amount {
type Output = Amount;
fn mul(self, other: f64) -> Self::Output {
let fac = (other * 100.).round() as i64;
Amount::fractions(self.value * fac / 100)
}
}
impl ops::Div<i64> for Amount {
type Output = Amount;
fn div(self, other: i64) -> Self::Output {
Amount::fractions(self.value / other)
}
}
impl ops::Div<i32> for Amount {
type Output = Amount;
fn div(self, other: i32) -> Self::Output {
Amount::fractions(self.value / i64::from(other))
}
}
impl ops::Div<u32> for &Amount {
type Output = Amount;
fn div(self, other: u32) -> Self::Output {
Amount::fractions(self.value / other as i64)
}
}
impl ops::Div<u8> for Amount {
type Output = Amount;
fn div(self, other: u8) -> Self::Output {
Amount::fractions(self.value / other as i64)
}
}
impl ops::Div<u8> for &Amount {
type Output = Amount;
fn div(self, other: u8) -> Self::Output {
Amount::fractions(self.value / other as i64)
}
}
impl<A> std::iter::Sum<A> for Amount
where
A: std::borrow::Borrow<Self>,
{
fn sum<I>(mut iter: I) -> Self
where
I: Iterator<Item = A>,
{
let first = if let Some(first) = iter.next() {
first
} else {
return Amount::fractions(0);
};
let init = *first.borrow();
iter.fold(init, |acc, e| acc + e.borrow())
}
}
impl cmp::Ord for Amount {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.value.cmp(&other.value)
}
}
impl cmp::PartialOrd for Amount {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::inconsistent_digit_grouping)]
use super::*;
#[test]
fn test_float_mul() {
assert_eq!(Amount::fractions(4000_00) * 0.9, Amount::fractions(3600_00));
}
#[test]
fn test_formatting() {
assert_eq!(Amount::fractions(123_456_00).to_string(), "SEK 123 456,00");
assert_eq!(Amount::fractions(1_234_56).to_string(), "SEK 1 234,56");
assert_eq!(Amount::fractions(1_456_00).to_string(), "SEK 1 456,00");
assert_eq!(Amount::fractions(14_56).to_string(), "SEK 14,56");
assert_eq!(Amount::fractions(156_00).to_string(), "SEK 156,00");
assert_eq!(Amount::fractions(156).to_string(), "SEK 1,56");
assert_eq!(Amount::fractions(999).to_string(), "SEK 9,99");
assert_eq!(Amount::fractions(999_00).to_string(), "SEK 999,00");
assert_eq!(Amount::fractions(10_01).to_string(), "SEK 10,01");
assert_eq!(Amount::fractions(1_001_00).to_string(), "SEK 1 001,00");
assert_eq!(Amount::fractions(-1_001_00).to_string(), "SEK -1 001,00");
assert_eq!(Amount::fractions(-10_01).to_string(), "SEK -10,01");
assert_eq!(Amount::fractions(9_123_00).to_string(), "SEK 9 123,00");
assert_eq!(Amount::fractions(91_23).to_string(), "SEK 91,23");
assert_eq!(Amount::fractions(92_568_07).to_string(), "SEK 92 568,07");
assert_eq!(
Amount::fractions(92_568_07_00).to_string(),
"SEK 9 256 807,00"
);
assert_eq!(
Amount::fractions(9_256_807_123_00).to_string(),
"SEK 9 256 807 123,00"
);
assert_eq!(
Amount::fractions(92_568_071_234_56).to_string(),
"SEK 92 568 071 234,56"
);
assert_eq!(
Amount::fractions(92_568_071_234_563_62).to_string(),
"SEK 92 568 071 234 563,62"
);
assert_eq!(
Amount::fractions(92_233_720_368_547_758_07).to_string(),
"SEK 92 233 720 368 547 758,07"
);
}
}