use std::fmt::Debug;
use super::super::*;
macro_rules! matchresult_from_comparison {
( $actual: ident $comparison: tt $expected: ident, $name: expr ) => {{
let builder = MatchResultBuilder::for_($name);
if $actual $comparison &$expected {
builder.matched()
} else {
builder.failed_comparison($actual, &$expected)
}
}}
}
pub fn assertion_always_succeeds<'a,T:'a>() -> Box<Matcher<'a,T> + 'a> {
Box::new(|_s: &T| MatchResultBuilder::for_("succeeds_always").matched())
}
pub fn any_value<'a,T:'a>() -> Box<Matcher<'a,T> + 'a> {
Box::new(|_s: &T| MatchResultBuilder::for_("any_value").matched())
}
pub fn assertion_always_fails<'a,T:'a>() -> Box<Matcher<'a,T> + 'a> {
Box::new(|_s: &T| {
MatchResultBuilder::for_("fails_always").failed_because("This matcher fails always")
})
}
pub fn no_value<'a,T:'a>() -> Box<Matcher<'a,T> + 'a> { assertion_always_fails() }
pub fn is<'a, T:'a>(matcher: Box<Matcher<'a,T> + 'a>) -> Box<Matcher<'a,T> + 'a> {
matcher
}
pub fn has<'a, T:'a>(matcher: Box<Matcher<'a,T> + 'a>) -> Box<Matcher<'a,T> + 'a> {
matcher
}
pub fn not<'a, T: 'a>(matcher: Box<Matcher<'a,T> + 'a>) -> Box<Matcher<'a,T> + 'a> {
Box::new(move |actual: &'a T| {
match matcher.check(actual) {
MatchResult::Matched { name } =>
MatchResultBuilder::for_(&format!("not({})", name))
.failed_because(&format!("{} is satisfied", name)),
MatchResult::Failed { name, .. } =>
MatchResultBuilder::for_(&format!("not({})", name)).matched()
}
})
}
pub fn equal_to<'a, T>(expected: T) -> Box<Matcher<'a,T> + 'a>
where T: PartialEq + Debug + 'a {
Box::new(move |actual: &T| matchresult_from_comparison!(actual == expected, "equal"))
}
pub fn eq<'a, T: PartialEq + Debug + 'a>(expected: T) -> Box<Matcher<'a,T> + 'a> { equal_to(expected) }
pub fn less_than<'a, T>(expected: T) -> Box<Matcher<'a,T> + 'a>
where T: PartialOrd + Debug + 'a {
Box::new(move |actual: &T| matchresult_from_comparison!(actual < expected, "less_than"))
}
pub fn lt<'a, T: PartialOrd + Debug + 'a>(expected: T) -> Box<Matcher<'a,T> + 'a> { less_than(expected) }
pub fn greater_than<'a, T>(expected: T) -> Box<Matcher<'a,T> + 'a>
where T: PartialOrd + Debug + 'a {
Box::new(move |actual: &T| matchresult_from_comparison!(actual > expected, "greater_than"))
}
pub fn gt<'a, T: PartialOrd + Debug + 'a>(expected: T) -> Box<Matcher<'a,T> + 'a> { greater_than(expected) }
pub fn less_than_or_equal<'a, T>(expected: T) -> Box<Matcher<'a,T> + 'a>
where T: PartialOrd + Debug + 'a {
Box::new(move |actual: &T| matchresult_from_comparison!(actual <= expected, "less_than_or_equal"))
}
pub fn leq<'a, T: PartialOrd + Debug + 'a>(expected: T) -> Box<Matcher<'a,T> + 'a> { less_than_or_equal(expected) }
pub fn greater_than_or_equal<'a, T>(expected: T) -> Box<Matcher<'a,T> + 'a>
where T: PartialOrd + Debug + 'a {
Box::new(move |actual: &T| matchresult_from_comparison!(actual >= expected, "greater_than_or_equal"))
}
pub fn geq<'a, T: PartialOrd + Debug + 'a>(expected: T) -> Box<Matcher<'a,T> + 'a> { greater_than_or_equal(expected) }
pub fn close_to<'a, T>(expected: T, eps: T) -> Box<Matcher<'a,T> + 'a>
where T: Copy + PartialOrd + std::ops::Add<Output=T> + std::ops::Sub<Output=T> + Debug + 'a {
Box::new(move |actual: &T| {
let builder = MatchResultBuilder::for_("close_to");
if &(expected - eps) <= actual && actual <= &(expected + eps) {
builder.matched()
} else {
builder.failed_because(&format!("{:?} should be between {:?} and {:?}",
actual, expected - eps, expected + eps)
)
}
})
}
pub fn same_object<'a, T>(expected: &'a T) -> Box<Matcher<'a,T> + 'a>
where T: Debug + 'a {
Box::new(move |actual: &T| {
let builder = MatchResultBuilder::for_("same_object");
if (actual as *const _) == (expected as *const _) {
builder.matched()
} else {
builder.failed_comparison(&actual, &expected)
}
})
}
#[macro_export]
macro_rules! has_structure {
( $variant:path { $( $field:ident : $matcher:expr ),* $(,)* } ) => { structure!($variant { $($field : $matcher),* }) };
( $variant:path [ $( $matchers:expr ),* ] ) => { structure!($variant [ $($matchers),* ]) }
}
#[macro_export]
macro_rules! structure {
( $variant:path { $( $field:ident : $matcher:expr ),* $(,)* } ) => {
Box::new(|actual: &_| {
use galvanic_assert::{MatchResultBuilder, MatchResult};
let builder = MatchResultBuilder::for_("has_structure");
#[allow(unreachable_patterns)]
match actual {
&$variant { $( ref $field, )* ..} => {
let mut failed_msgs = Vec::new();
$(
if let MatchResult::Failed{ name, reason } = $matcher.check($field) {
failed_msgs.push(
format!("Matcher '{}' for field '{}' at {}:{} failed:\n\t{}",
name, stringify!($field), file!().to_string(), line!(), reason)
);
}
)*
if failed_msgs.is_empty() { builder.matched() }
else { builder.failed_because(&failed_msgs.join("\n")) }
},
_ => builder.failed_because(
&format!("passed variant does not match '{}'", stringify!($variant))
)
}
})
};
(@expand ( $variant:path ; $field:ident ; $m:expr ; $($wildcard:tt),* ) -> ($($body:tt)*) ) => {
structure!(@generate ($field ; $($body)* ($m ; &$variant($($wildcard,)* ref $field))) )
};
(@expand ( $variant:path ; $field:ident ; $m:expr , $($matchers:expr),* ; $($wildcard:tt),* ) -> ($($body:tt)*) ) => {
structure!(@expand ( $variant ; $field ; $($matchers),* ; $($wildcard,)* _ ) -> ($($body)* ($m ; &$variant($($wildcard,)* ref $field, ..)),) )
};
(@generate ($field:ident ; $(($matcher:expr ; $pattern:pat)),*) ) => {
Box::new(|actual: &_| {
use galvanic_assert::{MatchResultBuilder, MatchResult};
let builder = MatchResultBuilder::for_("has_structure");
let mut failed_msgs = Vec::new();
$(
#[allow(unreachable_patterns)]
match actual {
$pattern => if let MatchResult::Failed{ name, reason } = $matcher.check($field) {
failed_msgs.push(
format!("Matcher '{}' for field '{}' at {}:{} failed:\n\t{}",
name, stringify!($field), file!().to_string(), line!(), reason)
);
},
_ => return builder.failed_because(
&format!("passed variant does not match '{}'", stringify!($variant))
)
}
)*
if failed_msgs.is_empty() { builder.matched() }
else { builder.failed_because(&failed_msgs.join("\n")) }
})
};
( $variant:path [ $( $matchers:expr ),* ] ) => {
structure![ @expand ( $variant ; x ; $($matchers),* ; ) -> () ]
};
}