#![allow(trivial_numeric_casts)]
use std::convert::From;
use std::{fmt, str, u64};
use std::str::FromStr;
use std::result::Result;
use std::error::Error;
use std::ops::{Add, AddAssign, Sub, SubAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign};
use std::num::ParseIntError;
use rustc_serialize::{Encodable, Decodable, Encoder, Decoder};
#[cfg(feature = "json-types")]
use rustc_serialize::json;
use super::CURRENCY_SYMBOL;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Amount {
value: u64,
}
impl Amount {
pub fn from_repr(value: u64) -> Amount {
Amount { value: value }
}
pub fn get_repr(&self) -> u64 {
self.value
}
pub fn min_value() -> Amount {
Amount { value: u64::MIN }
}
pub fn max_value() -> Amount {
Amount { value: u64::MAX }
}
}
#[cfg(feature = "json-types")]
impl json::ToJson for Amount {
fn to_json(&self) -> json::Json {
json::Json::F64(self.value as f64 / 1000.0)
}
}
impl fmt::Display for Amount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let units = self.value / 1_000;
let decimal_repr = self.value % 1_000;
let result = match f.precision() {
None => {
if decimal_repr == 0 {
format!("{}", units)
} else if decimal_repr % 100 == 0 {
format!("{}.{:01}", units, decimal_repr / 100)
} else if decimal_repr % 10 == 0 {
format!("{}.{:02}", units, decimal_repr / 10)
} else {
format!("{}.{:03}", units, decimal_repr)
}
}
Some(0) => {
format!("{}",
if decimal_repr >= 500 {
units + 1
} else {
units
})
}
Some(1) => {
format!("{}.{:01}",
units,
if decimal_repr % 100 >= 50 {
decimal_repr / 100 + 1
} else {
decimal_repr / 100
})
}
Some(2) => {
format!("{}.{:02}",
units,
if decimal_repr % 10 >= 5 {
decimal_repr / 10 + 1
} else {
decimal_repr / 10
})
}
Some(p) => {
let mut string = format!("{}.{:03}", units, decimal_repr);
for _ in 3..p {
string.push('0');
}
string
}
};
match f.width() {
None => write!(f, "{}", result),
Some(w) => {
if w < result.len() {
write!(f, "{}", result)
} else {
let mut pad = String::new();
for _ in result.len()..w {
pad.push('0');
}
write!(f, "{}{}", pad, result)
}
}
}
}
}
#[derive(Debug)]
pub struct AmountParseError {
description: String,
cause: Option<ParseIntError>,
}
impl AmountParseError {
fn new<S: AsRef<str>>(amount: S, error: S, cause: Option<ParseIntError>) -> AmountParseError {
AmountParseError {
description: format!("the amount {:?} is not a valid Fractal Global amount, {}",
amount.as_ref(),
error.as_ref()),
cause: cause,
}
}
}
impl fmt::Display for AmountParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description)
}
}
impl Error for AmountParseError {
fn description(&self) -> &str {
&self.description
}
fn cause(&self) -> Option<&Error> {
match self.cause.as_ref() {
Some(c) => Some(c),
None => None,
}
}
}
impl FromStr for Amount {
type Err = AmountParseError;
fn from_str(s: &str) -> Result<Amount, AmountParseError> {
if s.contains('.') {
let parts = s.split('.').count();
let mut split = s.split('.');
match parts {
2 => {
let units_str = split.next().unwrap();
let units: u64 = if units_str != "" {
match units_str.parse::<u64>() {
Ok(u) => {
if u <= u64::MAX / 1_000 {
u * 1_000
} else {
return Err(AmountParseError::new(s,
&format!("it is too big, the maximum amount is {}",
Amount::max_value()), None));
}
}
Err(e) => {
return Err(AmountParseError::new(s,
"the units part it is not a \
valid u64 amount",
Some(e)))
}
}
} else {
0
};
let mut decimals_str = String::from(split.next().unwrap());
if decimals_str.is_empty() {
return Err(AmountParseError::new(s,
"no decimals were found after the \
decimal separator",
None));
}
while decimals_str.len() < 3 {
decimals_str.push('0');
}
let decimals: u64 = match decimals_str.parse() {
Ok(d) => {
if decimals_str.len() == 3 {
d
} else {
let divisor = 10u64.pow(decimals_str.len() as u32 - 3);
let rem = d % divisor;
if rem >= divisor / 2 {
d / divisor + 1
} else {
d / divisor
}
}
}
Err(_) => {
return Err(AmountParseError::new(s,
"the decimal part is not a valid \
u64 number",
None))
}
};
if (u64::MAX - decimals) >= units {
Ok(Amount::from_repr(units + decimals))
} else {
Err(AmountParseError::new(s,
&format!("it is too big, the maximum amount \
is {}",
Amount::max_value()),
None))
}
}
_ => {
Err(AmountParseError::new(s,
"an amount can only have one period to separate \
units and decimals",
None))
}
}
} else {
match s.parse::<u64>() {
Ok(v) => {
if v <= u64::MAX / 1_000 {
Ok(Amount::from_repr(v * 1_000))
} else {
Err(AmountParseError::new(s,
&format!("it is too big, the maximum amount \
is {}",
Amount::max_value()),
None))
}
}
Err(_) => Err(AmountParseError::new(s, "it is not a valid u64 number", None)),
}
}
}
}
impl fmt::Debug for Amount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,
"Amount {{ {:?} }} ({} {})",
self.value,
CURRENCY_SYMBOL,
self)
}
}
impl Encodable for Amount {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_u64(self.value)
}
}
impl Decodable for Amount {
fn decode<D: Decoder>(d: &mut D) -> Result<Amount, D::Error> {
match d.read_u64() {
Ok(repr) => Ok(Amount::from_repr(repr)),
Err(e) => Err(e),
}
}
}
macro_rules! impl_ops_int {
($($t:ty)*) => ($(
impl Div<$t> for Amount {
type Output = Amount;
fn div(self, rhs: $t) -> Amount {
Amount { value: self.value / rhs as u64 }
}
}
impl DivAssign<$t> for Amount {
fn div_assign(&mut self, rhs: $t) {
self.value /= rhs as u64
}
}
impl Rem<$t> for Amount {
type Output = Amount;
fn rem(self, rhs: $t) -> Amount {
Amount { value: self.value % (rhs as u64 * 1_000)}
}
}
impl RemAssign<$t> for Amount {
fn rem_assign(&mut self, rhs: $t) {
self.value %= rhs as u64 * 1_000
}
}
impl Mul<$t> for Amount {
type Output = Amount;
fn mul(self, rhs: $t) -> Amount {
Amount { value: self.value * rhs as u64 }
}
}
impl Mul<Amount> for $t {
type Output = Amount;
fn mul(self, rhs: Amount) -> Amount {
Amount { value: self as u64 * rhs.value }
}
}
impl MulAssign<$t> for Amount {
fn mul_assign(&mut self, rhs: $t) {
self.value *= rhs as u64
}
}
)*)
}
impl_ops_int! { u8 u16 u32 u64 usize }
impl Add for Amount {
type Output = Amount;
fn add(self, rhs: Amount) -> Amount {
Amount { value: self.value + rhs.value }
}
}
impl AddAssign for Amount {
fn add_assign(&mut self, rhs: Amount) {
self.value += rhs.value
}
}
impl Sub for Amount {
type Output = Amount;
fn sub(self, rhs: Amount) -> Amount {
Amount { value: self.value - rhs.value }
}
}
impl SubAssign for Amount {
fn sub_assign(&mut self, rhs: Amount) {
self.value -= rhs.value
}
}