use std::fmt;
use crate::MatchError;
#[derive(Debug)]
#[non_exhaustive]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub struct SoftErrors {
collected: Vec<MatchError>,
}
impl SoftErrors {
pub const fn new() -> Self {
Self {
collected: Vec::new(),
}
}
pub fn check(&mut self, result: Result<(), MatchError>) {
if let Err(e) = result {
self.collected.push(e);
}
}
pub fn finish(self) -> Result<(), SoftMatchError> {
if self.collected.is_empty() {
return Ok(());
}
Err(SoftMatchError {
errors: self.collected,
})
}
pub fn is_empty(&self) -> bool {
self.collected.is_empty()
}
pub fn len(&self) -> usize {
self.collected.len()
}
pub fn errors(&self) -> &[MatchError] {
&self.collected
}
}
impl Default for SoftErrors {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
#[non_exhaustive]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub struct SoftMatchError {
pub errors: Vec<MatchError>,
}
impl fmt::Display for SoftMatchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let count = self.errors.len();
let noun = if count == 1 {
"assertion"
} else {
"assertions"
};
writeln!(f, "{count} soft {noun} failed:")?;
for (i, err) in self.errors.iter().enumerate() {
write!(f, "\n[{}] {err}", i + 1)?;
if i + 1 < count {
writeln!(f)?;
}
}
Ok(())
}
}
impl std::error::Error for SoftMatchError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_is_empty() {
let errors = SoftErrors::new();
assert!(errors.is_empty());
assert_eq!(errors.len(), 0);
}
#[test]
fn check_collects_errors() {
let mut errors = SoftErrors::new();
errors.check(Ok(()));
assert!(errors.is_empty());
let err = MatchError::new("x".to_string(), "1".to_string(), "2".to_string(), false);
errors.check(Err(err));
assert_eq!(errors.len(), 1);
assert!(!errors.is_empty());
}
#[test]
fn finish_ok_when_empty() {
let errors = SoftErrors::new();
assert!(errors.finish().is_ok());
}
#[test]
fn finish_err_when_failures() {
let mut errors = SoftErrors::new();
let err = MatchError::new("x".to_string(), "1".to_string(), "2".to_string(), false);
errors.check(Err(err));
let result = errors.finish();
assert!(result.is_err());
}
#[test]
fn display_format() {
let mut errors = SoftErrors::new();
errors.check(Err(MatchError::new(
"name".to_string(),
"to not be empty".to_string(),
"\"\"".to_string(),
false,
)));
errors.check(Err(MatchError::new(
"age".to_string(),
"to be greater than 0".to_string(),
"-1".to_string(),
false,
)));
let result = errors.finish();
assert!(result.is_err());
let msg = result.err().map(|e| e.to_string()).unwrap_or_default();
assert!(msg.contains("2 soft assertions failed:"));
assert!(msg.contains("[1]"));
assert!(msg.contains("[2]"));
assert!(msg.contains("expect!(name)"));
assert!(msg.contains("expect!(age)"));
}
#[test]
fn errors_returns_slice() {
let mut errors = SoftErrors::new();
errors.check(Err(MatchError::new(
"x".to_string(),
"a".to_string(),
"b".to_string(),
false,
)));
assert_eq!(errors.errors().len(), 1);
assert_eq!(errors.errors()[0].expression, "x");
}
#[test]
fn default_is_empty() {
let errors = SoftErrors::default();
assert!(errors.is_empty());
}
}