use crate::argument::Argument;
use crate::fmt::MaybeDebug;
use crate::fmt::MaybeDebugWrapper;
use crate::matcher::ArgumentMatcher;
use nameof::name_of;
use nearly_eq::NearlyEq;
use std::fmt::{self, Debug, Display};
impl Argument {
pub fn nearly_eq<T, U>(&self, value: T) -> NearlyEqArgumentMatcher<T, U>
where
T: NearlyEq<T, U> + MaybeDebug,
U: MaybeDebug,
{
NearlyEqArgumentMatcher {
value,
accuracy: T::eps(),
}
}
pub fn nearly_eq_with_accuracy<T, U>(
&self,
value: T,
accuracy: U,
) -> NearlyEqArgumentMatcher<T, U>
where
T: NearlyEq<T, U> + MaybeDebug,
U: MaybeDebug,
{
NearlyEqArgumentMatcher { value, accuracy }
}
}
pub struct NearlyEqArgumentMatcher<T, U>
where
T: NearlyEq<T, U> + MaybeDebug,
U: MaybeDebug,
{
value: T,
accuracy: U,
}
impl<T, U> Display for NearlyEqArgumentMatcher<T, U>
where
T: NearlyEq<T, U> + MaybeDebug,
U: MaybeDebug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{:?}±{:?}",
MaybeDebugWrapper(&self.value),
MaybeDebugWrapper(&self.accuracy)
)
}
}
impl<T, U> Debug for NearlyEqArgumentMatcher<T, U>
where
T: NearlyEq<T, U> + MaybeDebug,
U: MaybeDebug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(name_of!(type NearlyEqArgumentMatcher<T, U>))
.field(name_of!(value in Self), &MaybeDebugWrapper(&self.value))
.field(
name_of!(accuracy in Self),
&MaybeDebugWrapper(&self.accuracy),
)
.finish()
}
}
impl<T, U> ArgumentMatcher<T> for NearlyEqArgumentMatcher<T, U>
where
T: NearlyEq<T, U> + MaybeDebug,
U: MaybeDebug,
{
fn matches_argument(&self, input: &T) -> bool {
NearlyEq::eq(&self.value, input, &self.accuracy)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn float_is_not_nearly_equivalent_to_different_float() {
let factory = Argument::internal_new();
let first_value = 3.0;
let second_value = first_value + 1.0;
assert!(!factory
.nearly_eq(first_value)
.matches_argument(&second_value));
}
#[test]
fn float_is_nearly_equivalent_to_slightly_different_float() {
let factory = Argument::internal_new();
let first_value = 3.0;
let second_value = first_value + 0.000_000_000_000_1;
assert!(factory
.nearly_eq(first_value)
.matches_argument(&second_value));
}
#[test]
fn float_is_nearly_equal_to_itself() {
let factory = Argument::internal_new();
let first_value = 3.0;
let second_value = first_value;
assert!(factory
.nearly_eq(first_value)
.matches_argument(&second_value));
}
#[test]
fn float_is_not_nearly_equal_to_different_float_with_no_accuracy() {
let factory = Argument::internal_new();
let first_value = 3.0;
let second_value = first_value + 1.0;
let accuracy = 0.0;
assert!(!factory
.nearly_eq_with_accuracy(first_value, accuracy)
.matches_argument(&second_value));
}
#[test]
fn float_is_not_nearly_equal_to_different_float_with_same_accuracy_as_difference() {
let factory = Argument::internal_new();
let first_value = 3.0;
let second_value = first_value + 0.1;
let accuracy = 0.1;
assert!(!factory
.nearly_eq_with_accuracy(first_value, accuracy)
.matches_argument(&second_value));
}
#[test]
fn float_is_nearly_equal_to_different_float_with_low_accuracy() {
let factory = Argument::internal_new();
let first_value = 3.0;
let second_value = first_value + 0.01;
let accuracy = 0.1;
assert!(factory
.nearly_eq_with_accuracy(first_value, accuracy)
.matches_argument(&second_value));
}
#[test]
fn float_is_not_nearly_equal_to_different_float_with_highest_accuracy() {
let factory = Argument::internal_new();
let first_value = 3.0;
let second_value = first_value + 0.1;
let accuracy = 0.0;
assert!(!factory
.nearly_eq_with_accuracy(first_value, accuracy)
.matches_argument(&second_value));
}
#[test]
fn float_is_nearly_equal_to_itself_with_highest_accuracy() {
let factory = Argument::internal_new();
let first_value = 3.0;
let second_value = first_value;
let accuracy = 0.0;
assert!(factory
.nearly_eq_with_accuracy(first_value, accuracy)
.matches_argument(&second_value));
}
}