//! Working with remote error types using [`Accumulated`]. use crate::FusedError; use std::{ borrow::{Borrow, BorrowMut}, fmt::{self, Debug, Display}, ops::{Deref, DerefMut, Index, IndexMut}, slice::SliceIndex, }; /// A thin wrapper to handle remote error types as if they implemented /// [`FusedError`]. /// /// # Examples /// /// ``` /// use fused_error::{Accumulated, Accumulator}; /// /// // `Accumulated` is needed since Rust's "orphan rule" won't allow you to /// // implement `FusedError` on `String`: /// type Error = Accumulated; /// /// let mut acc = Accumulator::::new(); /// /// // A downside from using `Accumulated` is that methods that would /// // implicitly perform conversions before only accept `E`, `Vec`, /// // and `Accumulated`: /// acc.push("foo".to_string()); /// acc.push("bar".to_string()); /// /// assert_eq!(acc.finish().unwrap_err(), ["foo", "bar"]); /// ``` #[derive(Clone, Eq, PartialOrd, Ord, Hash)] pub struct Accumulated { inner: Vec, } impl Accumulated { /// Constructs a new, empty `Accumulated`. /// /// # Examples /// /// ``` /// use fused_error::Accumulated; /// /// let mut err = Accumulated::new(); /// # err.push("foo"); /// ``` #[must_use] #[inline] pub fn new() -> Self { Accumulated::from_vec(Vec::new()) } /// Constructs a new `Accumulated` from a vector. /// /// # Examples /// /// ``` /// use fused_error::Accumulated; /// /// let mut err = Accumulated::from_vec(vec!["foo", "bar"]); /// ``` #[must_use] #[inline] pub fn from_vec(vec: Vec) -> Self { Accumulated { inner: vec } } /// Unwraps the `Accumulated` into the underlying vector. /// /// # Examples /// /// ``` /// use fused_error::Accumulated; /// /// let mut err = Accumulated::new(); /// err.push("foo"); /// err.push("bar"); /// /// assert_eq!(err.into_vec(), ["foo", "bar"]); /// ``` #[must_use] #[inline] pub fn into_vec(self) -> Vec { self.inner } } impl FusedError for Accumulated { #[inline] fn combine(&mut self, mut other: Self) { self.inner.append(&mut other.inner); } } impl Default for Accumulated { #[must_use] #[inline] fn default() -> Self { Accumulated::new() } } //////////////////////////////////////////////////////////////////////////////// // Deref traits //////////////////////////////////////////////////////////////////////////////// impl Deref for Accumulated { type Target = Vec; #[inline] fn deref(&self) -> &Self::Target { &self.inner } } impl DerefMut for Accumulated { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } //////////////////////////////////////////////////////////////////////////////// // Error traits //////////////////////////////////////////////////////////////////////////////// impl Debug for Accumulated where E: Debug, { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(&self.inner, f) } } impl Display for Accumulated where E: Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let messages = self.iter().map(ToString::to_string).collect::>(); write!( f, "accumulated collection of {} errors: {messages:#?}", self.inner.len() ) } } impl std::error::Error for Accumulated where E: Debug + Display {} //////////////////////////////////////////////////////////////////////////////// // Conversion traits //////////////////////////////////////////////////////////////////////////////// impl From> for Accumulated { #[inline] fn from(vec: Vec) -> Self { Accumulated::from_vec(vec) } } impl From> for Vec { #[inline] fn from(acc: Accumulated) -> Self { acc.into_vec() } } impl From for Accumulated { #[inline] fn from(err: E) -> Self { Accumulated::from_vec(vec![err]) } } //////////////////////////////////////////////////////////////////////////////// // Iterator traits //////////////////////////////////////////////////////////////////////////////// impl FromIterator for Accumulated { #[inline] fn from_iter>(iter: T) -> Self { let vec = Vec::from_iter(iter); Accumulated::from_vec(vec) } } impl Extend for Accumulated { #[inline] fn extend>(&mut self, iter: T) { self.inner.extend(iter); } } impl<'a, E> IntoIterator for &'a Accumulated { type Item = &'a E; type IntoIter = Iter<'a, E>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a, E> IntoIterator for &'a mut Accumulated { type Item = &'a mut E; type IntoIter = IterMut<'a, E>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } impl IntoIterator for Accumulated { type Item = E; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { self.inner.into_iter() } } //////////////////////////////////////////////////////////////////////////////// // Iterator types //////////////////////////////////////////////////////////////////////////////// /// Immutable `Accumulated` iterator. pub type Iter<'a, E> = std::slice::Iter<'a, E>; /// Mutable `Accumulated` iterator. pub type IterMut<'a, E> = std::slice::IterMut<'a, E>; /// An iterator that moves out of an `Accumulated`. pub type IntoIter = std::vec::IntoIter; //////////////////////////////////////////////////////////////////////////////// // As traits //////////////////////////////////////////////////////////////////////////////// impl AsRef<[E]> for Accumulated { #[inline] fn as_ref(&self) -> &[E] { self.inner.as_ref() } } impl AsMut<[E]> for Accumulated { #[inline] fn as_mut(&mut self) -> &mut [E] { self.inner.as_mut() } } impl Borrow<[E]> for Accumulated { #[inline] fn borrow(&self) -> &[E] { self.inner.deref().borrow() } } impl BorrowMut<[E]> for Accumulated { #[inline] fn borrow_mut(&mut self) -> &mut [E] { self.inner.deref_mut().borrow_mut() } } //////////////////////////////////////////////////////////////////////////////// // Index traits //////////////////////////////////////////////////////////////////////////////// impl Index for Accumulated where I: SliceIndex<[E]>, { type Output = I::Output; #[inline] fn index(&self, index: I) -> &Self::Output { self.inner.deref().index(index) } } impl IndexMut for Accumulated where I: SliceIndex<[E]>, { #[inline] fn index_mut(&mut self, index: I) -> &mut Self::Output { self.inner.deref_mut().index_mut(index) } } //////////////////////////////////////////////////////////////////////////////// // Comparison traits //////////////////////////////////////////////////////////////////////////////// macro_rules! impl_slice_eq { ([$($vars:tt)*] $lhs:ty, $rhs:ty) => { impl PartialEq<$rhs> for $lhs where E: PartialEq { #[inline] fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } } } } impl_slice_eq!([] Accumulated, Accumulated); impl_slice_eq!([] Accumulated, Vec); impl_slice_eq!([] Vec, Accumulated); impl_slice_eq!([] Accumulated, [F]); impl_slice_eq!([][E], Accumulated); impl_slice_eq!([] Accumulated, &[F]); impl_slice_eq!([] & [E], Accumulated); impl_slice_eq!([const N: usize] Accumulated, [F; N]); impl_slice_eq!([const N: usize] [E; N], Accumulated); impl_slice_eq!([const N: usize] Accumulated, &[F; N]); impl_slice_eq!([const N: usize] &[E; N], Accumulated); impl PartialOrd> for Accumulated where E: PartialOrd, { #[inline] fn partial_cmp(&self, other: &Vec) -> Option { self.inner.partial_cmp(other) } } impl PartialOrd> for Vec where E: PartialOrd, { #[inline] fn partial_cmp(&self, other: &Accumulated) -> Option { self.partial_cmp(&other.inner) } }