use alloc::string::String;
use alloc::vec::Vec;
use core::fmt::{Arguments, Write};
use crate::{
AssertThat,
details::{DetailMessages, WithDetail},
prelude::Mode,
};
pub trait Failure {
fn write_to(self, target: &mut String) -> std::fmt::Result;
}
impl Failure for &str {
fn write_to(self, target: &mut String) -> core::fmt::Result {
target.write_str(self)
}
}
impl Failure for Arguments<'_> {
fn write_to(self, target: &mut String) -> core::fmt::Result {
target.write_fmt(self)
}
}
impl<F> Failure for F
where
F: FnOnce(&mut String) -> core::fmt::Result,
{
fn write_to(self, target: &mut String) -> core::fmt::Result {
self(target)
}
}
pub(crate) trait Fallible {
fn store_failure(&self, failure: String);
}
impl<T, M: Mode> Fallible for AssertThat<'_, T, M> {
fn store_failure(&self, failure: String) {
match &self.parent {
Some(parent) => parent.store_failure(failure),
None => self.failures.borrow_mut().push(failure),
}
}
}
impl<T, M: Mode> AssertThat<'_, T, M> {
#[track_caller]
pub fn fail(&self, failure: impl Failure) {
let mut detail_messages = Vec::new();
self.collect_messages(&mut detail_messages);
let msg = build_failure_message(
self.print_location,
self.subject_name.as_deref(),
&detail_messages,
failure,
)
.expect("no write error");
if self.mode.borrow().is_capture() {
self.store_failure(msg);
} else {
panic!("{msg}");
}
}
}
#[track_caller]
fn build_failure_message(
print_location: bool,
subject_name: Option<&str>,
detail_messages: &[String],
failure: impl Failure,
) -> Result<String, core::fmt::Error> {
let mut err = String::new();
err.write_str("-------- assertr --------\n")?;
if print_location {
let caller_location = core::panic::Location::caller();
let _ = err.write_fmt(format_args!(
"Assertion failed at {file}:{line}:{column}\n\n",
file = caller_location.file(),
line = caller_location.line(),
column = caller_location.column(),
));
}
if let Some(subject_name) = subject_name {
err.write_fmt(format_args!("Subject: {subject_name}\n\n"))?;
}
failure.write_to(&mut err)?;
if !detail_messages.is_empty() {
err.write_str("\n")?;
let detail_messages = DetailMessages(detail_messages);
err.write_fmt(format_args!("Details: {detail_messages:#?}\n"))?;
}
err.write_str("-------- assertr --------\n")?;
Ok(err)
}