use crate::FusedError;
use std::fmt::{self, Debug};
#[must_use = "accumulators will panic on drop if not handled"]
pub(crate) struct Accumulator<E> {
pub errors: Option<Vec<E>>,
}
impl<E> Accumulator<E> {
#[inline]
pub fn new() -> Self {
Accumulator::from_vec(Vec::new())
}
#[inline]
pub fn from_vec(vec: Vec<E>) -> Self {
Accumulator { errors: Some(vec) }
}
#[inline]
#[track_caller]
fn check(&self) {
assert!(
!(cfg!(debug_assertions) && self.is_handled()),
"cannot access accumulator after it's been handled"
);
}
#[must_use]
#[inline]
#[track_caller]
pub fn as_ref(&self) -> &Vec<E> {
self.check();
unsafe { self.errors.as_ref().unwrap_unchecked() }
}
#[must_use]
#[inline]
#[track_caller]
pub fn as_mut(&mut self) -> &mut Vec<E> {
self.check();
unsafe { self.errors.as_mut().unwrap_unchecked() }
}
#[inline]
#[track_caller]
pub fn push(&mut self, err: E) {
self.as_mut().push(err);
}
#[must_use]
#[inline]
#[track_caller]
pub unsafe fn take(&mut self) -> Vec<E> {
self.check();
self.errors.take().unwrap_unchecked()
}
#[must_use]
#[inline]
#[track_caller]
pub unsafe fn reduce(&mut self) -> Option<E>
where
E: FusedError,
{
self.take().into_iter().reduce(FusedError::merge)
}
#[inline]
#[track_caller]
pub unsafe fn ignore(&mut self) {
self.check();
self.errors = None;
}
#[must_use]
#[inline]
pub fn is_handled(&self) -> bool {
self.errors.is_none()
}
}
impl<E> Debug for Accumulator<E>
where
E: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.errors {
Some(ref errors) => Debug::fmt(errors, f),
None => f.write_str("Accumulator(None)"),
}
}
}
impl<E> Drop for Accumulator<E> {
fn drop(&mut self) {
if cfg!(debug_assertions) {
if let Some(ref errors) = self.errors {
let len = errors.len();
match len {
0 => panic!("fused_error::Accumulator dropped without getting handled"),
n => panic!(
"fused_error::Accumulator dropped with {n} unhandled error{}",
if n == 1 { "" } else { "s" },
),
}
}
}
}
}