asserting 0.14.0

Fluent assertions for tests in Rust that are convenient to write and easy to extend.
Documentation
use crate::properties::{
    AdditiveIdentityProperty, InfinityProperty, IsNanProperty, MultiplicativeIdentityProperty,
    SignumProperty,
};

impl SignumProperty for f32 {
    fn is_negative_property(&self) -> bool {
        self.is_sign_negative()
    }

    fn is_positive_property(&self) -> bool {
        self.is_sign_positive() && *self != 0.
    }
}

impl SignumProperty for f64 {
    fn is_negative_property(&self) -> bool {
        self.is_sign_negative()
    }

    fn is_positive_property(&self) -> bool {
        self.is_sign_positive() && *self != 0.
    }
}

macro_rules! impl_additive_identity_property {
    ($type:ty) => {
        impl AdditiveIdentityProperty for $type {
            fn additive_identity() -> Self {
                0.
            }
        }

        impl AdditiveIdentityProperty for &$type {
            fn additive_identity() -> Self {
                &0.
            }
        }
    };
}

impl_additive_identity_property!(f32);
impl_additive_identity_property!(f64);

macro_rules! impl_multiplicative_identity_property {
    ($type:ty) => {
        impl MultiplicativeIdentityProperty for $type {
            fn multiplicative_identity() -> Self {
                1.
            }
        }

        impl MultiplicativeIdentityProperty for &$type {
            fn multiplicative_identity() -> Self {
                &1.
            }
        }
    };
}

impl_multiplicative_identity_property!(f32);
impl_multiplicative_identity_property!(f64);

impl InfinityProperty for f32 {
    fn is_infinite_property(&self) -> bool {
        self.is_infinite()
    }

    fn is_finite_property(&self) -> bool {
        self.is_finite()
    }
}

impl InfinityProperty for f64 {
    fn is_infinite_property(&self) -> bool {
        self.is_infinite()
    }

    fn is_finite_property(&self) -> bool {
        self.is_finite()
    }
}

impl IsNanProperty for f32 {
    fn is_nan_property(&self) -> bool {
        self.is_nan()
    }
}

impl IsNanProperty for f64 {
    fn is_nan_property(&self) -> bool {
        self.is_nan()
    }
}

#[cfg(feature = "float-cmp")]
mod cmp {
    use crate::assertions::{AssertIsCloseToWithDefaultMargin, AssertIsCloseToWithinMargin};
    use crate::colored::mark_diff;
    use crate::expectations::{is_close_to, not, IsCloseTo};
    use crate::spec::{DiffFormat, Expectation, Expression, FailingStrategy, Invertible, Spec};
    use crate::std::{format, string::String};
    use float_cmp::{ApproxEq, F32Margin, F64Margin};

    impl<R> AssertIsCloseToWithDefaultMargin<f32> for Spec<'_, f32, R>
    where
        R: FailingStrategy,
    {
        fn is_close_to(self, expected: f32) -> Self {
            self.expecting(is_close_to(expected).within_margin((4. * f32::EPSILON, 4)))
        }

        fn is_not_close_to(self, expected: f32) -> Self {
            self.expecting(not(
                is_close_to(expected).within_margin((4. * f32::EPSILON, 4))
            ))
        }
    }

    impl<R> AssertIsCloseToWithinMargin<f32, F32Margin> for Spec<'_, f32, R>
    where
        R: FailingStrategy,
    {
        fn is_close_to_with_margin(self, expected: f32, margin: impl Into<F32Margin>) -> Self {
            self.expecting(is_close_to(expected).within_margin(margin))
        }

        fn is_not_close_to_with_margin(self, expected: f32, margin: impl Into<F32Margin>) -> Self {
            self.expecting(not(is_close_to(expected).within_margin(margin)))
        }
    }

    impl<R> AssertIsCloseToWithDefaultMargin<f64> for Spec<'_, f64, R>
    where
        R: FailingStrategy,
    {
        fn is_close_to(self, expected: f64) -> Self {
            self.expecting(is_close_to(expected).within_margin((4. * f64::EPSILON, 4)))
        }

        fn is_not_close_to(self, expected: f64) -> Self {
            self.expecting(not(
                is_close_to(expected).within_margin((4. * f64::EPSILON, 4))
            ))
        }
    }

    impl<R> AssertIsCloseToWithinMargin<f64, F64Margin> for Spec<'_, f64, R>
    where
        R: FailingStrategy,
    {
        fn is_close_to_with_margin(self, expected: f64, margin: impl Into<F64Margin>) -> Self {
            self.expecting(is_close_to(expected).within_margin(margin))
        }

        fn is_not_close_to_with_margin(self, expected: f64, margin: impl Into<F64Margin>) -> Self {
            self.expecting(not(is_close_to(expected).within_margin(margin)))
        }
    }

    impl Expectation<f32> for IsCloseTo<f32, F32Margin> {
        fn test(&mut self, subject: &f32) -> bool {
            subject.approx_eq(self.expected, self.margin)
        }

        fn message(
            &self,
            expression: &Expression<'_>,
            actual: &f32,
            inverted: bool,
            format: &DiffFormat,
        ) -> String {
            let not = if inverted { "not " } else { "" };
            let (marked_actual, marked_expected) = mark_diff(actual, &self.expected, format);
            format!("expected {expression} to be {not}close to {:?}\n  within a margin of epsilon={:e} and ulps={}\n   but was: {marked_actual}\n  expected: {marked_expected}",
                &self.expected, self.margin.epsilon, self.margin.ulps
            )
        }
    }

    impl Invertible for IsCloseTo<f32, F32Margin> {}

    impl Expectation<f64> for IsCloseTo<f64, F64Margin> {
        fn test(&mut self, subject: &f64) -> bool {
            subject.approx_eq(self.expected, self.margin)
        }

        fn message(
            &self,
            expression: &Expression<'_>,
            actual: &f64,
            inverted: bool,
            format: &DiffFormat,
        ) -> String {
            let not = if inverted { "not " } else { "" };
            let (marked_actual, marked_expected) = mark_diff(actual, &self.expected, format);
            format!("expected {expression} to be {not}close to {:?}\n  within a margin of epsilon={:e} and ulps={}\n   but was: {marked_actual}\n  expected: {marked_expected}",
                &self.expected, self.margin.epsilon, self.margin.ulps
            )
        }
    }

    impl Invertible for IsCloseTo<f64, F64Margin> {}
}

#[cfg(test)]
mod tests;