mod equation;
mod retry;
mod soft;
use std::fmt::Debug;
pub use equation::{
EnergyVerifier, EquationContext, EquationResult, EquationVerifier, InvariantVerifier,
KinematicVerifier, MomentumVerifier, Variable,
};
pub use retry::{
retry_contains, retry_eq, retry_none, retry_some, retry_true, AssertionCheckResult,
RetryAssertion, RetryConfig, RetryError, RetryResult,
};
pub use soft::{
AssertionFailure, AssertionMode, AssertionSummary, SoftAssertionError, SoftAssertions,
};
#[derive(Debug, Clone)]
pub struct AssertionResult {
pub passed: bool,
pub message: String,
}
impl AssertionResult {
#[must_use]
pub const fn pass() -> Self {
Self {
passed: true,
message: String::new(),
}
}
#[must_use]
pub fn fail(message: impl Into<String>) -> Self {
Self {
passed: false,
message: message.into(),
}
}
}
#[derive(Debug)]
pub struct Assertion;
impl Assertion {
#[must_use]
pub fn equals<T: PartialEq + Debug>(expected: &T, actual: &T) -> AssertionResult {
contract_pre_assertion_evaluation!();
if expected == actual {
AssertionResult::pass()
} else {
AssertionResult::fail(format!("expected {expected:?}, got {actual:?}"))
}
}
#[must_use]
pub fn contains(haystack: &str, needle: &str) -> AssertionResult {
if haystack.contains(needle) {
AssertionResult::pass()
} else {
AssertionResult::fail(format!("expected '{haystack}' to contain '{needle}'"))
}
}
#[must_use]
pub fn in_range(value: f64, min: f64, max: f64) -> AssertionResult {
contract_pre_assertion_evaluation!();
if value >= min && value <= max {
AssertionResult::pass()
} else {
AssertionResult::fail(format!("expected {value} to be in range [{min}, {max}]"))
}
}
#[must_use]
pub fn is_true(condition: bool, message: &str) -> AssertionResult {
if condition {
AssertionResult::pass()
} else {
AssertionResult::fail(message)
}
}
#[must_use]
pub fn is_false(condition: bool, message: &str) -> AssertionResult {
if condition {
AssertionResult::fail(message)
} else {
AssertionResult::pass()
}
}
#[must_use]
pub fn is_some<T>(opt: &Option<T>) -> AssertionResult {
if opt.is_some() {
AssertionResult::pass()
} else {
AssertionResult::fail("expected Some, got None")
}
}
#[must_use]
pub fn is_none<T>(opt: &Option<T>) -> AssertionResult {
if opt.is_none() {
AssertionResult::pass()
} else {
AssertionResult::fail("expected None, got Some")
}
}
#[must_use]
pub fn is_ok<T, E>(result: &Result<T, E>) -> AssertionResult {
if result.is_ok() {
AssertionResult::pass()
} else {
AssertionResult::fail("expected Ok, got Err")
}
}
#[must_use]
pub fn is_err<T, E>(result: &Result<T, E>) -> AssertionResult {
if result.is_err() {
AssertionResult::pass()
} else {
AssertionResult::fail("expected Err, got Ok")
}
}
#[must_use]
pub fn approx_eq(a: f64, b: f64, epsilon: f64) -> AssertionResult {
contract_pre_assertion_evaluation!();
if (a - b).abs() < epsilon {
AssertionResult::pass()
} else {
AssertionResult::fail(format!("expected {a} ≈ {b} (epsilon: {epsilon})"))
}
}
#[must_use]
pub fn has_length<T>(collection: &[T], expected: usize) -> AssertionResult {
if collection.len() == expected {
AssertionResult::pass()
} else {
AssertionResult::fail(format!(
"expected length {expected}, got {}",
collection.len()
))
}
}
}