Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,19 @@ for floating point numbers of type `f32` and `f64`:
| is_not_a_number | verify that the subject is not a number |
| is_a_number | verify that the subject is a number |

### Decimal number

for decimal numbers of types

* `bigdecimal:BigDecimal` and `bigdecimal:BigDecimalRef` (requires crate feature `bigdecimal`)
* `rust_decimal::Decimal` (requires crate feature `rust-decimal`)

| assertion | description |
|------------------|----------------------------------------------------|
| has_scale_of | verify that the subject has the expected scale |
| has_precision_of | verify that the subject has the expected precision |
| is_integer | verify that the subject has zero fractional digits |

### Float comparison

for floating point numbers of type `f32` and `f64`.
Expand Down
136 changes: 136 additions & 0 deletions src/assertions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,142 @@ pub trait AssertNotANumber {
fn is_a_number(self) -> Self;
}

/// Assert decimal number specific properties.
pub trait AssertDecimalNumber {
/// Verifies the scale of a decimal number.
///
/// It compares the scale, the total number of digits to the right of the
/// decimal point (including insignificant leading zeros), to the expected
/// scale.
///
/// # Examples
///
/// For `bigdecimal::BigDecimal` (requires crate feature `bigdecimal`):
///
/// ```
/// # #[cfg(not(feature = "bigdecimal"))]
/// # fn main() {}
/// # #[cfg(feature = "bigdecimal")]
/// # fn main() {
/// use asserting::prelude::*;
/// use bigdecimal::BigDecimal;
///
/// let subject: BigDecimal = "42.0839".parse().unwrap();
/// assert_that!(subject).has_scale_of(4);
///
/// let subject: BigDecimal = "1.053700".parse().unwrap();
/// assert_that!(&subject).has_scale_of(6);
/// assert_that!(subject.normalized()).has_scale_of(4);
/// # }
/// ```
///
/// For `rust_decimal::Decimal` (requires crate feature `rust-decimal`):
///
/// ```
/// # #[cfg(not(feature = "rust-decimal"))]
/// # fn main() {}
/// # #[cfg(feature = "rust-decimal")]
/// # fn main() {
/// use asserting::prelude::*;
/// use rust_decimal::Decimal;
///
/// let subject: Decimal = "42.0839".parse().unwrap();
/// assert_that!(subject).has_scale_of(4);
///
/// let subject: Decimal = "1.053700".parse().unwrap();
/// assert_that!(subject).has_scale_of(6);
/// assert_that!(subject.normalize()).has_scale_of(4);
/// # }
/// ```
#[track_caller]
fn has_scale_of(self, expected_scale: i64) -> Self;

/// Verifies the precision of a decimal number.
///
/// It compares the precision, the total number of digits in the non-scaled
/// integer representation, to the expected precision.
///
/// # Examples
///
/// For `bigdecimal::BigDecimal` (requires crate feature `bigdecimal`):
///
/// ```
/// # #[cfg(not(feature = "bigdecimal"))]
/// # fn main() {}
/// # #[cfg(feature = "bigdecimal")]
/// # fn main() {
/// use asserting::prelude::*;
/// use bigdecimal::BigDecimal;
///
/// let subject: BigDecimal = "42.0839".parse().unwrap();
///
/// assert_that!(subject).has_precision_of(6);
/// # }
/// ```
///
/// For `rust_decimal::Decimal` (requires crate feature `rust-decimal`):
///
/// ```
/// # #[cfg(not(feature = "rust-decimal"))]
/// # fn main() {}
/// # #[cfg(feature = "rust-decimal")]
/// # fn main() {
/// use asserting::prelude::*;
/// use rust_decimal::Decimal;
///
/// let subject: Decimal = "42.083916".parse().unwrap();
/// assert_that!(subject).has_precision_of(29);
///
/// let subject: Decimal = "1.05".parse().unwrap();
/// assert_that!(subject).has_precision_of(29);
/// # }
/// ```
///
/// Note: `rust_decimal::Decimal` is fixed precision decimal number. The
/// actual precision is always 29.
#[track_caller]
fn has_precision_of(self, expected_precision: u64) -> Self;

/// Verifies that a decimal number has zero fractional digits (is equivalent
/// to an integer).
///
/// # Examples
///
/// For `bigdecimal::BigDecimal` (requires crate feature `bigdecimal`):
///
/// ```
/// # #[cfg(not(feature = "bigdecimal"))]
/// # fn main() {}
/// # #[cfg(feature = "bigdecimal")]
/// # fn main() {
/// use asserting::prelude::*;
/// use bigdecimal::BigDecimal;
///
/// let subject: BigDecimal = "14_752.0".parse().unwrap();
///
/// assert_that!(subject).is_integer();
/// # }
/// ```
///
/// For `rust_decimal::Decimal` (requires crate feature `rust-decimal`):
///
/// ```
/// # #[cfg(not(feature = "rust-decimal"))]
/// # fn main() {}
/// # #[cfg(feature = "rust-decimal")]
/// # fn main() {
/// use asserting::prelude::*;
/// use rust_decimal::Decimal;
///
/// let subject: Decimal = "14_752.0".parse().unwrap();
///
/// assert_that!(subject).is_integer();
/// # }
/// ```
#[track_caller]
fn is_integer(self) -> Self;
}

/// Assert whether some value or expression is true or false.
///
/// # Examples
Expand Down
18 changes: 17 additions & 1 deletion src/bigdecimal/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::properties::{AdditiveIdentityProperty, MultiplicativeIdentityProperty, SignumProperty};
use crate::properties::{
AdditiveIdentityProperty, DecimalProperties, MultiplicativeIdentityProperty, SignumProperty,
};
use bigdecimal::num_bigint::Sign;
use bigdecimal::{BigDecimal, BigDecimalRef, One, Zero};
use lazy_static::lazy_static;
Expand Down Expand Up @@ -52,6 +54,20 @@ impl MultiplicativeIdentityProperty for &BigDecimal {
}
}

impl DecimalProperties for BigDecimal {
fn precision_property(&self) -> u64 {
self.digits()
}

fn scale_property(&self) -> i64 {
self.fractional_digit_count()
}

fn is_integer_property(&self) -> bool {
self.is_integer()
}
}

impl SignumProperty for BigDecimalRef<'_> {
fn is_negative_property(&self) -> bool {
self.sign() == Sign::Minus
Expand Down
144 changes: 144 additions & 0 deletions src/bigdecimal/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,147 @@ fn bigdecimalref_is_zero() {
fn bigdecimalref_is_one() {
assert_that(BigDecimal::new(BigInt::from(1), 0).to_ref()).is_one();
}

#[test]
fn bigdecimal_has_precision_of() {
let subject = BigDecimal::new(BigInt::from(420_831), 4);

assert_that(subject).has_precision_of(6);
}

#[test]
fn bigdecimal_has_precision_of_trailing_zeros() {
let subject = BigDecimal::new(BigInt::from(420_831_000), 7);

assert_that(&subject).has_precision_of(9);

assert_that(subject.normalized()).has_precision_of(6);
}

#[test]
fn verify_bigdecimal_has_precision_of_fails() {
let subject = BigDecimal::new(BigInt::from(420_831_000), 7);

let failures = verify_that(subject).has_precision_of(7).display_failures();

assert_eq!(
failures,
&[
r"assertion failed: expected subject to have a precision of 7
but was: 9
expected: 7
"
]
);
}

#[test]
fn bigdecimal_has_scale_of() {
let subject = BigDecimal::new(BigInt::from(420_831), 4);

assert_that(subject).has_scale_of(4);
}

#[test]
fn bigdecimal_has_scale_of_with_zero_in_fraction() {
let subject = BigDecimal::new(BigInt::from(420_830), 1);

assert_that(&subject).has_scale_of(1);

assert_that(subject.normalized()).has_scale_of(0);
}

#[test]
fn verify_bigdecimal_has_scale_of_fails() {
let subject = BigDecimal::new(BigInt::from(420_831_000), 5);

let failures = verify_that(subject.normalized())
.has_scale_of(5)
.display_failures();

assert_eq!(
failures,
&[r"assertion failed: expected subject to have a scale of 5
but was: 2
expected: 5
"]
);
}

#[test]
fn bigdecimal_has_scale_of_trailing_zeros() {
let subject = BigDecimal::new(BigInt::from(420_831_000), 4);

assert_that(subject).has_scale_of(4);
}

#[test]
fn bigdecimal_is_integer() {
let subject = BigDecimal::new(BigInt::from(420_830), 0);

assert_that(subject).is_integer();
}

#[test]
fn bigdecimal_is_integer_zero_in_fraction() {
let subject = BigDecimal::new(BigInt::from(420_830), 1);

assert_that(subject).is_integer();
}

#[test]
fn verify_bigdecimal_is_integer_fails() {
let subject = BigDecimal::new(BigInt::from(420_810), 2);

let failures = verify_that(subject).is_integer().display_failures();

assert_eq!(
failures,
&[r"assertion failed: expected subject to be an integer value
but was: BigDecimal(sign=Plus, scale=2, digits=[420810])
expected: an integer value
"]
);
}

#[test]
fn borrowed_bigdecimal_has_precision_of() {
let subject = BigDecimal::new(BigInt::from(420_831), 4);

assert_that(&subject).has_precision_of(6);
}

#[test]
fn borrowed_bigdecimal_has_scale_of() {
let subject = BigDecimal::new(BigInt::from(420_831), 4);

assert_that(&subject).has_scale_of(4);
}

#[test]
fn borrowed_bigdecimal_is_integer() {
let subject = BigDecimal::new(BigInt::from(420_830), 0);

assert_that(&subject).is_integer();
}

#[test]
fn mutable_borrowed_bigdecimal_has_precision_of() {
let mut subject = BigDecimal::new(BigInt::from(420_831), 4);

assert_that(&mut subject).has_precision_of(6);
}

#[test]
fn mutable_borrowed_bigdecimal_has_scale_of() {
let mut subject = BigDecimal::new(BigInt::from(420_831), 4);

assert_that(&mut subject).has_scale_of(4);
}

#[test]
fn mutable_borrowed_bigdecimal_is_integer() {
let mut subject = BigDecimal::new(BigInt::from(420_830), 0);

assert_that(&mut subject).is_integer();
}
13 changes: 13 additions & 0 deletions src/expectations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,19 @@ pub struct IsNotANumber;
#[must_use]
pub struct IsANumber;

#[must_use]
pub struct HasPrecisionOf {
pub expected_precision: u64,
}

#[must_use]
pub struct HasScaleOf {
pub expected_scale: i64,
}

#[must_use]
pub struct IsInteger;

#[must_use]
pub struct IsLowerCase;

Expand Down
Loading
Loading