use super::*;
macro_rules! impl_safe_convert_decimal_to_int {
($($dst:ty),*) => {
$(
impl SafeConvert<$dst> for Decimal {
fn checked_convert(self) -> Option<$dst> {
if let Some(int_part) = self.inner().to_bigint() {
<$dst>::try_from(int_part).ok()
} else {
None
}
}
fn saturating_convert(self) -> $dst {
if let Some(int_part) = self.inner().to_bigint() {
if let Ok(val) = <$dst>::try_from(&int_part) {
val
} else if int_part < BigInt::from(0) {
<$dst>::MIN
} else {
<$dst>::MAX
}
} else {
0
}
}
fn wrapping_convert(self) -> $dst {
if let Some(int_part) = self.inner().to_bigint() {
if let Ok(val) = <$dst>::try_from(&int_part) {
val
} else {
self.saturating_convert()
}
} else {
0
}
}
}
)*
};
}
fn decimal_to_f64(decimal: &Decimal) -> f64 {
format!("{:e}", decimal.inner())
.parse::<f64>()
.expect("BigDecimal LowerExp always emits parseable f64 scientific notation")
}
macro_rules! impl_safe_convert_decimal_to_float {
($($dst:ty),*) => {
$(
impl SafeConvert<$dst> for Decimal {
fn checked_convert(self) -> Option<$dst> {
let f = decimal_to_f64(&self);
if !f.is_finite() {
return None;
}
if f < <$dst>::MIN as f64 || f > <$dst>::MAX as f64 {
return None;
}
Some(f as $dst)
}
fn saturating_convert(self) -> $dst {
let f = decimal_to_f64(&self);
if !f.is_finite() {
return if f.is_sign_negative() { <$dst>::MIN } else { <$dst>::MAX };
}
if f < <$dst>::MIN as f64 {
return <$dst>::MIN;
}
if f > <$dst>::MAX as f64 {
return <$dst>::MAX;
}
f as $dst
}
fn wrapping_convert(self) -> $dst {
self.saturating_convert()
}
}
)*
};
}
impl_safe_convert_decimal_to_int!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
impl_safe_convert_decimal_to_float!(f32, f64);
impl SafeConvert<Int> for Decimal {
fn checked_convert(self) -> Option<Int> {
self.inner().to_bigint().map(Int)
}
fn saturating_convert(self) -> Int {
self.checked_convert().unwrap_or(Int::zero())
}
fn wrapping_convert(self) -> Int {
self.saturating_convert()
}
}
impl SafeConvert<Uint> for Decimal {
fn checked_convert(self) -> Option<Uint> {
if let Some(big_int) = self.inner().to_bigint() {
if big_int >= BigInt::from(0) {
Some(Uint(big_int))
} else {
None
}
} else {
None
}
}
fn saturating_convert(self) -> Uint {
if let Some(big_int) = self.inner().to_bigint() {
if big_int >= BigInt::from(0) {
Uint(big_int)
} else {
Uint::zero()
}
} else {
Uint::zero()
}
}
fn wrapping_convert(self) -> Uint {
if let Some(big_int) = self.inner().to_bigint() {
Uint(big_int.abs())
} else {
Uint::zero()
}
}
}
#[cfg(test)]
pub mod tests {
mod i8 {
use super::*;
use crate::value::{decimal::Decimal, number::safe::convert::SafeConvert};
#[test]
fn test_checked_convert() {
let x = Decimal::from(127i64);
let y: Option<i8> = x.checked_convert();
assert_eq!(y, Some(127i8));
}
#[test]
fn test_checked_convert_overflow() {
let x = Decimal::from(128i64);
let y: Option<i8> = x.checked_convert();
assert_eq!(y, None);
}
#[test]
fn test_saturating_convert() {
let x = Decimal::from(200i64);
let y: i8 = x.saturating_convert();
assert_eq!(y, i8::MAX);
}
#[test]
fn test_wrapping_convert() {
let x = Decimal::from(-129i64);
let y: i8 = x.wrapping_convert();
assert_eq!(y, i8::MIN);
}
}
mod i32 {
use super::*;
use crate::value::{decimal::Decimal, number::safe::convert::SafeConvert};
#[test]
fn test_checked_convert() {
let x = Decimal::from(2147483647i64);
let y: Option<i32> = x.checked_convert();
assert_eq!(y, Some(2147483647i32));
}
#[test]
fn test_saturating_convert() {
let x = Decimal::from(-2147483648i64);
let y: i32 = x.saturating_convert();
assert_eq!(y, -2147483648i32);
}
}
mod u8 {
use super::*;
use crate::value::{decimal::Decimal, number::safe::convert::SafeConvert};
#[test]
fn test_checked_convert() {
let x = Decimal::from(255i64);
let y: Option<u8> = x.checked_convert();
assert_eq!(y, Some(255u8));
}
#[test]
fn test_checked_convert_overflow() {
let x = Decimal::from(256i64);
let y: Option<u8> = x.checked_convert();
assert_eq!(y, None);
}
#[test]
fn test_checked_convert_negative() {
let x = Decimal::from(-1i64);
let y: Option<u8> = x.checked_convert();
assert_eq!(y, None);
}
#[test]
fn test_saturating_convert() {
let x = Decimal::from(1000i64);
let y: u8 = x.saturating_convert();
assert_eq!(y, u8::MAX);
}
}
mod u32 {
use super::*;
use crate::value::{decimal::Decimal, number::safe::convert::SafeConvert};
#[test]
fn test_checked_convert() {
let x = Decimal::from(4294967295i64);
let y: Option<u32> = x.checked_convert();
assert_eq!(y, Some(4294967295u32));
}
#[test]
fn test_saturating_convert() {
let x = Decimal::from(-100i64);
let y: u32 = x.saturating_convert();
assert_eq!(y, 0u32);
}
}
mod f32 {
use std::str::FromStr;
use bigdecimal::BigDecimal;
use super::*;
use crate::value::{decimal::Decimal, number::safe::convert::SafeConvert};
#[test]
fn test_checked_convert() {
let x = Decimal::from(42i64);
let y: Option<f32> = x.checked_convert();
assert_eq!(y, Some(42.0f32));
}
#[test]
fn test_saturating_convert() {
let x = Decimal::from(-1000i64);
let y: f32 = x.saturating_convert();
assert_eq!(y, -1000.0f32);
}
#[test]
fn checked_convert_f32_max_exact_literal_roundtrips() {
let bd = BigDecimal::from_str("3.4028234663852886e38").unwrap();
let dec = Decimal::new(bd);
let out: Option<f32> = dec.checked_convert();
assert_eq!(out, Some(f32::MAX));
}
#[test]
fn checked_convert_neg_f32_max_exact_literal_roundtrips() {
let bd = BigDecimal::from_str("-3.4028234663852886e38").unwrap();
let dec = Decimal::new(bd);
let out: Option<f32> = dec.checked_convert();
assert_eq!(out, Some(f32::MIN));
}
#[test]
fn checked_convert_rejects_value_just_above_f32_max() {
let bd = BigDecimal::from_str("3.4028235e38").unwrap();
let dec = Decimal::new(bd);
let out: Option<f32> = dec.checked_convert();
assert_eq!(out, None);
}
#[test]
fn checked_convert_rejects_value_above_f32_max() {
let bd = BigDecimal::from_str("1e40").unwrap();
let dec = Decimal::new(bd);
let out: Option<f32> = dec.checked_convert();
assert_eq!(out, None);
}
#[test]
fn saturating_convert_above_f32_max_returns_max() {
let bd = BigDecimal::from_str("1e40").unwrap();
let dec = Decimal::new(bd);
let out: f32 = dec.saturating_convert();
assert_eq!(out, f32::MAX);
}
#[test]
fn saturating_convert_below_neg_f32_max_returns_min() {
let bd = BigDecimal::from_str("-1e40").unwrap();
let dec = Decimal::new(bd);
let out: f32 = dec.saturating_convert();
assert_eq!(out, f32::MIN);
}
#[test]
fn checked_convert_f32_min_positive_roundtrips() {
let bd = BigDecimal::from_str("1.17549435e-38").unwrap();
let dec = Decimal::new(bd);
let out: Option<f32> = dec.checked_convert();
assert_eq!(out, Some(f32::MIN_POSITIVE));
}
}
mod f64 {
use std::str::FromStr;
use bigdecimal::BigDecimal;
use super::*;
use crate::value::{decimal::Decimal, number::safe::convert::SafeConvert};
#[test]
fn test_checked_convert() {
let x = Decimal::from(42i64);
let y: Option<f64> = x.checked_convert();
assert_eq!(y, Some(42.0f64));
}
#[test]
fn test_saturating_convert() {
let x = Decimal::from(-1000i64);
let y: f64 = x.saturating_convert();
assert_eq!(y, -1000.0f64);
}
#[test]
fn checked_convert_f64_max_literal_roundtrips() {
let bd = BigDecimal::from_str("1.7976931348623157e308").unwrap();
let dec = Decimal::new(bd);
let out: Option<f64> = dec.checked_convert();
assert_eq!(out, Some(f64::MAX));
}
#[test]
fn checked_convert_neg_f64_max_literal_roundtrips() {
let bd = BigDecimal::from_str("-1.7976931348623157e308").unwrap();
let dec = Decimal::new(bd);
let out: Option<f64> = dec.checked_convert();
assert_eq!(out, Some(f64::MIN));
}
#[test]
fn checked_convert_rejects_value_above_f64_max() {
let bd = BigDecimal::from_str("1e400").unwrap();
let dec = Decimal::new(bd);
let out: Option<f64> = dec.checked_convert();
assert_eq!(out, None);
}
#[test]
fn saturating_convert_above_f64_max_returns_max() {
let bd = BigDecimal::from_str("1e400").unwrap();
let dec = Decimal::new(bd);
let out: f64 = dec.saturating_convert();
assert_eq!(out, f64::MAX);
}
#[test]
fn saturating_convert_below_neg_f64_max_returns_min() {
let bd = BigDecimal::from_str("-1e400").unwrap();
let dec = Decimal::new(bd);
let out: f64 = dec.saturating_convert();
assert_eq!(out, f64::MIN);
}
#[test]
fn checked_convert_f64_min_positive_roundtrips() {
let bd = BigDecimal::from_str("2.2250738585072014e-308").unwrap();
let dec = Decimal::new(bd);
let out: Option<f64> = dec.checked_convert();
assert_eq!(out, Some(f64::MIN_POSITIVE));
}
}
mod int {
use crate::value::{decimal::Decimal, int::Int, number::safe::convert::SafeConvert};
#[test]
fn test_checked_convert() {
let x = Decimal::from(12345i64);
let y: Option<Int> = x.checked_convert();
assert!(y.is_some());
assert_eq!(y.unwrap().to_string(), "12345");
}
#[test]
fn test_saturating_convert() {
let x = Decimal::from(-999999i64);
let y: Int = x.saturating_convert();
assert_eq!(y.to_string(), "-999999");
}
#[test]
fn test_wrapping_convert() {
let x = Decimal::from(0i64);
let y: Int = x.wrapping_convert();
assert_eq!(y.to_string(), "0");
}
}
mod uint {
use crate::value::{decimal::Decimal, number::safe::convert::SafeConvert, uint::Uint};
#[test]
fn test_checked_convert_positive() {
let x = Decimal::from(42i64);
let y: Option<Uint> = x.checked_convert();
assert!(y.is_some());
assert_eq!(y.unwrap().to_string(), "42");
}
#[test]
fn test_checked_convert_negative() {
let x = Decimal::from(-1i64);
let y: Option<Uint> = x.checked_convert();
assert!(y.is_none());
}
#[test]
fn test_saturating_convert() {
let x = Decimal::from(-100i64);
let y: Uint = x.saturating_convert();
assert_eq!(y.to_string(), "0");
}
#[test]
fn test_wrapping_convert() {
let x = Decimal::from(-1i64);
let y: Uint = x.wrapping_convert();
assert_eq!(y.to_string(), "1");
}
}
mod self_conversion {
use crate::value::{decimal::Decimal, number::safe::convert::SafeConvert};
#[test]
fn test_checked_convert() {
let x = Decimal::from(42i64);
let y: Option<Decimal> = x.clone().checked_convert();
assert_eq!(y, Some(x));
}
#[test]
fn test_saturating_convert() {
let x = Decimal::from(-100i64);
let y: Decimal = x.clone().saturating_convert();
assert_eq!(y, x);
}
#[test]
fn test_wrapping_convert() {
let x = Decimal::from(999i64);
let y: Decimal = x.clone().wrapping_convert();
assert_eq!(y, x);
}
}
}