app_error/
multiple.rs

1//! Multiple error handling
2
3// Imports
4use {
5	crate::AppError,
6	itertools::Itertools,
7	std::{
8		fmt,
9		ops::{ControlFlow, FromResidual, Try},
10	},
11};
12
13/// Helper type to collect a `IntoIter<Item = Result<T, AppError>>`
14/// into a `Result<C, AppError>` with all of the errors instead of the first.
15pub enum AllErrs<C, D = ()> {
16	Ok(C),
17	Err(Vec<AppError<D>>),
18}
19
20impl<C, D> fmt::Debug for AllErrs<C, D>
21where
22	C: fmt::Debug,
23	D: fmt::Debug + 'static,
24{
25	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26		match self {
27			Self::Ok(value) => f.debug_tuple("Ok").field(value).finish(),
28			Self::Err(errs) => f.debug_tuple("Err").field(errs).finish(),
29		}
30	}
31}
32
33impl<C, D> Default for AllErrs<C, D>
34where
35	C: Default,
36{
37	fn default() -> Self {
38		Self::Ok(C::default())
39	}
40}
41impl<C, U, D> Extend<Result<U, AppError<D>>> for AllErrs<C, D>
42where
43	C: Extend<U>,
44{
45	fn extend<T: IntoIterator<Item = Result<U, AppError<D>>>>(&mut self, iter: T) {
46		// TODO: Do this more efficiently?
47		for res in iter {
48			match (&mut *self, res) {
49				// If we have a collection, and we get an item, extend it
50				(Self::Ok(collection), Ok(item)) => collection.extend_one(item),
51				// If we have a collection, but find an error, switch to errors
52				(Self::Ok(_), Err(err)) => *self = Self::Err(vec![err]),
53				// If we have errors and got an item, ignore it
54				(Self::Err(_), Ok(_)) => (),
55				// If we have errors and got an error, extend it
56				(Self::Err(errs), Err(err)) => errs.push(err),
57			}
58		}
59	}
60}
61
62impl<C, T, D> FromIterator<Result<T, AppError<D>>> for AllErrs<C, D>
63where
64	C: Default + Extend<T>,
65{
66	fn from_iter<I>(iter: I) -> Self
67	where
68		I: IntoIterator<Item = Result<T, AppError<D>>>,
69	{
70		// TODO: If we get any errors, don't allocate memory for the rest of the values?
71		let (values, errs) = iter.into_iter().partition_result::<C, Vec<_>, _, _>();
72		match errs.is_empty() {
73			true => Self::Ok(values),
74			false => Self::Err(errs),
75		}
76	}
77}
78
79pub struct AllErrsResidue<D>(Vec<AppError<D>>);
80
81impl<D> fmt::Debug for AllErrsResidue<D>
82where
83	D: fmt::Debug + 'static,
84{
85	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86		f.debug_tuple("AllErrsResidue").field(&self.0).finish()
87	}
88}
89
90impl<C, D> Try for AllErrs<C, D> {
91	type Output = C;
92	type Residual = AllErrsResidue<D>;
93
94	fn from_output(output: Self::Output) -> Self {
95		Self::Ok(output)
96	}
97
98	fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
99		match self {
100			Self::Ok(values) => ControlFlow::Continue(values),
101			Self::Err(errs) => ControlFlow::Break(AllErrsResidue(errs)),
102		}
103	}
104}
105
106impl<T, D> FromResidual<AllErrsResidue<D>> for AllErrs<T, D> {
107	fn from_residual(residual: AllErrsResidue<D>) -> Self {
108		Self::Err(residual.0)
109	}
110}
111
112impl<T, D> FromResidual<AllErrsResidue<D>> for Result<T, AppError<D>> {
113	fn from_residual(residual: AllErrsResidue<D>) -> Self {
114		let err = match <[_; 1]>::try_from(residual.0) {
115			Ok([err]) => err,
116			Err(errs) => {
117				assert!(!errs.is_empty(), "`ResultMultipleResidue` should hold at least 1 error");
118				AppError::from_multiple(errs)
119			},
120		};
121
122		Err(err)
123	}
124}