#![cfg_attr(doc, doc = include_str!("../README.md"))]
#![cfg_attr(not(feature = "std"), no_std)]
pub mod assertion;
pub use assertion::Assertion;
use assertion::{BinaryAssertion, UnaryAssertion};
use core::fmt::{Debug, Display};
use core::ops::Deref;
#[derive(Debug, Clone, Copy)]
#[must_use = "this blank assertion will do nothing unless it is tested"]
pub struct Assert<T> {
this: T,
info: AssertInfo,
}
#[derive(Debug, Clone, Copy)]
pub struct AssertInfo {
file: &'static str,
line: u32,
column: u32,
}
impl AssertInfo {
const fn reassert<T>(self, val: T) -> Assert<T> {
Assert {
this: val,
info: self,
}
}
}
impl Display for AssertInfo {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let Self { file, line, column } = *self;
write!(f, "{file}:{line}:{column}")
}
}
#[macro_export]
macro_rules! assert_that {
($this:expr) => {
$crate::Assert::manual_constructor(
$this,
::core::file!(),
::core::line!(),
::core::column!(),
)
};
}
impl<T> Assert<T> {
#[doc(hidden)]
pub const fn manual_constructor(this: T, file: &'static str, line: u32, column: u32) -> Self {
Self {
this,
info: AssertInfo { file, line, column },
}
}
pub const fn as_ref(&self) -> Assert<&T> {
Assert {
this: &self.this,
info: self.info,
}
}
pub fn as_deref<U>(&self) -> Assert<&U>
where
T: Deref<Target = U>,
{
Assert {
this: &self.this,
info: self.info,
}
}
pub fn map<F, U>(self, f: F) -> Assert<U>
where
F: FnOnce(T) -> U,
{
self.info.reassert(f(self.this))
}
pub fn is_equal_to<U>(self, other: U) -> BinaryAssertion<T, U>
where
T: PartialEq<U> + Debug,
U: Debug,
{
let test_result = self.this == other;
BinaryAssertion::new(self.this, other, test_result, "lhs == rhs", self.info)
}
pub fn is_not_equal_to<U>(self, other: U) -> BinaryAssertion<T, U>
where
T: PartialEq<U> + Debug,
U: Debug,
{
let test_result = self.this != other;
BinaryAssertion::new(self.this, other, test_result, "lhs != rhs", self.info)
}
pub fn is_greater_than<U>(self, other: U) -> BinaryAssertion<T, U>
where
T: PartialOrd<U> + Debug,
U: Debug,
{
let test_result = self.this > other;
BinaryAssertion::new(self.this, other, test_result, "lhs > rhs", self.info)
}
pub fn is_less_than<U>(self, other: U) -> BinaryAssertion<T, U>
where
T: PartialOrd<U> + Debug,
U: Debug,
{
let test_result = self.this < other;
BinaryAssertion::new(self.this, other, test_result, "lhs < rhs", self.info)
}
pub fn is_greater_than_or_equal_to<U>(self, other: U) -> BinaryAssertion<T, U>
where
T: PartialOrd<U> + Debug,
U: Debug,
{
let test_result = self.this >= other;
BinaryAssertion::new(self.this, other, test_result, "lhs >= rhs", self.info)
}
pub fn is_less_than_or_equal_to<U>(self, other: U) -> BinaryAssertion<T, U>
where
T: PartialOrd<U> + Debug,
U: Debug,
{
let test_result = self.this <= other;
BinaryAssertion::new(self.this, other, test_result, "lhs <= rhs", self.info)
}
pub fn is_in(self, container: &[T]) -> BinaryAssertion<&[T], T>
where
T: PartialEq,
{
self.info.reassert(container).contains(self.this)
}
pub fn is_not_in(self, container: &[T]) -> BinaryAssertion<&[T], T>
where
T: PartialEq,
{
self.info.reassert(container).does_not_contain(self.this)
}
}
impl<T> Assert<&T> {
pub fn cloned(self) -> Assert<T>
where
T: Clone,
{
Assert {
this: self.this.clone(),
info: self.info,
}
}
pub const fn copied(self) -> Assert<T>
where
T: Copy,
{
Assert {
this: *self.this,
info: self.info,
}
}
}
impl Assert<bool> {
pub const fn is_true(self) -> UnaryAssertion<bool> {
UnaryAssertion::new(self.this, self.this, "this.is_true()", self.info)
}
pub const fn is_false(self) -> UnaryAssertion<bool> {
UnaryAssertion::new(self.this, !self.this, "this.is_false()", self.info)
}
}
impl<T: Debug> Assert<Option<T>> {
pub fn is_some(self) -> UnaryAssertion<Option<T>> {
let test_result = self.this.is_some();
UnaryAssertion::new(self.this, test_result, "this.is_some()", self.info)
}
pub fn is_none(self) -> UnaryAssertion<Option<T>> {
let test_result = self.this.is_none();
UnaryAssertion::new(self.this, test_result, "this.is_none()", self.info)
}
pub fn is_some_and<F, A>(self, f: F) -> UnaryAssertion<Option<T>>
where
F: Fn(Assert<&T>) -> A,
A: Assertion,
{
let test_result = self
.this
.as_ref()
.is_some_and(|opt| f(self.info.reassert(opt)).test());
UnaryAssertion::new(self.this, test_result, "this.is_some_and(f)", self.info)
}
pub fn is_none_or<F, A>(self, f: F) -> UnaryAssertion<Option<T>>
where
F: Fn(Assert<&T>) -> A,
A: Assertion,
{
let test_result = self
.this
.as_ref()
.is_none_or(|opt| f(self.info.reassert(opt)).test());
UnaryAssertion::new(self.this, test_result, "this.is_none_or(f)", self.info)
}
}
impl<T: Debug, E: Debug> Assert<Result<T, E>> {
pub fn is_ok(self) -> UnaryAssertion<Result<T, E>> {
let test_result = self.this.is_ok();
UnaryAssertion::new(self.this, test_result, "this.is_ok()", self.info)
}
pub fn is_err(self) -> UnaryAssertion<Result<T, E>> {
let test_result = self.this.is_err();
UnaryAssertion::new(self.this, test_result, "this.is_err()", self.info)
}
pub fn is_ok_and<F, A>(self, f: F) -> impl Assertion
where
F: Fn(Assert<&T>) -> A,
A: Assertion,
{
let test_result = self
.this
.as_ref()
.is_ok_and(|o| f(self.info.reassert(o)).test());
UnaryAssertion::new(self.this, test_result, "this.is_ok_and(f)", self.info)
}
pub fn is_err_and<F, A>(self, f: F) -> UnaryAssertion<Result<T, E>>
where
F: Fn(Assert<&E>) -> A,
A: Assertion,
{
let test_result = self
.this
.as_ref()
.is_err_and(|e| f(self.info.reassert(e)).test());
UnaryAssertion::new(self.this, test_result, "this.is_err_and(f)", self.info)
}
}
impl<'a, T> Assert<&'a [T]> {
pub fn is_sorted(self) -> UnaryAssertion<&'a [T]>
where
T: PartialOrd,
{
let test_result = self.this.is_sorted();
UnaryAssertion::new(self.this, test_result, "this.is_sorted()", self.info)
}
pub fn is_not_sorted(self) -> UnaryAssertion<&'a [T]>
where
T: PartialOrd,
{
let test_result = !self.this.is_sorted();
UnaryAssertion::new(self.this, test_result, "!this.is_sorted()", self.info)
}
pub fn is_sorted_by<'b, F>(self, compare: F) -> UnaryAssertion<&'a [T]>
where
F: FnMut(&'b T, &'b T) -> bool,
'a: 'b, {
let test_result = self.this.is_sorted_by(compare);
UnaryAssertion::new(self.this, test_result, "this.is_sorted_by()", self.info)
}
pub fn is_not_sorted_by<'b, F>(self, compare: F) -> UnaryAssertion<&'a [T]>
where
F: FnMut(&'b T, &'b T) -> bool,
'a: 'b, {
let test_result = !self.this.is_sorted_by(compare);
UnaryAssertion::new(self.this, test_result, "!this.is_sorted_by()", self.info)
}
pub fn is_sorted_by_key<'b, F, K>(self, f: F) -> UnaryAssertion<&'a [T]>
where
F: FnMut(&'b T) -> K,
K: PartialOrd,
'a: 'b,
{
let test_result = self.this.is_sorted_by_key(f);
UnaryAssertion::new(self.this, test_result, "this.is_sorted_by_key()", self.info)
}
pub fn is_not_sorted_by_key<'b, F, K>(self, f: F) -> UnaryAssertion<&'a [T]>
where
F: FnMut(&'b T) -> K,
K: PartialOrd,
'a: 'b,
{
let test_result = !self.this.is_sorted_by_key(f);
UnaryAssertion::new(
self.this,
test_result,
"!this.is_sorted_by_key()",
self.info,
)
}
pub const fn is_empty(self) -> UnaryAssertion<&'a [T]> {
let test_result = self.this.is_empty();
UnaryAssertion::new(self.this, test_result, "this.is_empty()", self.info)
}
pub const fn is_not_empty(self) -> UnaryAssertion<&'a [T]> {
let test_result = !self.this.is_empty();
UnaryAssertion::new(self.this, test_result, "!this.is_empty()", self.info)
}
pub const fn is_len(self, len: usize) -> BinaryAssertion<&'a [T], usize> {
let test_result = self.this.len() == len;
BinaryAssertion::new(self.this, len, test_result, "lhs.is_len(rhs)", self.info)
}
pub const fn is_not_len(self, len: usize) -> BinaryAssertion<&'a [T], usize> {
let test_result = self.this.len() != len;
BinaryAssertion::new(self.this, len, test_result, "!lhs.is_len(rhs)", self.info)
}
pub fn contains(self, elem: T) -> BinaryAssertion<&'a [T], T>
where
T: PartialEq,
{
let test_result = self.this.contains(&elem);
BinaryAssertion::new(self.this, elem, test_result, "lhs.contains(rhs)", self.info)
}
pub fn does_not_contain(self, elem: T) -> BinaryAssertion<&'a [T], T>
where
T: PartialEq,
{
let test_result = !self.this.contains(&elem);
BinaryAssertion::new(
self.this,
elem,
test_result,
"!lhs.contains(rhs)",
self.info,
)
}
}