fused_error/accumulator/mod.rs
1//! Error accumulation via [`Accumulator<E>`].
2//!
3//! Error accumulators are useful for collecting multiple errors throughout an
4//! operation to provide more holistic diagnostics or to defer stages of error
5//! handling.
6//!
7//! # Panics
8//!
9//! Accumulators will panic on drop if not handled correctly **even if empty**:
10//!
11//! ```should_panic
12//! use fused_error::Accumulator;
13//!
14//! let mut acc = Accumulator::<&str>::new();
15//!
16//! // We dropped an accumulator without handling it, so panic!
17//! ```
18//!
19//! To prevent it from panicking on drop, you must terminate an accumulator with
20//! any of the methods that take `self` by value. For a comprehensive overview,
21//! look at the "Termination" section.
22//!
23//! The accumulator panicking, despite the annoying error stack trace, is a
24//! massive safety net for the developer. It's possible that an accumulator
25//! could store tens, hundreds, even thousands of errors. To drop an accumulator
26//! and lose them silently and implicitly could be catastrophic.
27//!
28//! # Examples
29//!
30//! Basic usage:
31//!
32//! ```
33//! use fused_error::Accumulator;
34//!
35//! // Note that the turbofish operator is typically required for `new`:
36//! let mut acc = Accumulator::<String>::new();
37//!
38//! // Any method that collects errors performs conversions identical to the
39//! // question mark operator, `?`:
40//! acc.push(String::from("foo"));
41//! acc.push("bar");
42//!
43//! assert_eq!(acc.len(), 2);
44//!
45//! // Conditionally push errors as further diagnostics only if necessary. If
46//! // the accumulator was empty, this would do nothing:
47//! acc.trace("baz");
48//!
49//! assert_eq!(acc.len(), 3);
50//!
51//! let results = vec![
52//! Ok(1),
53//! Ok(2),
54//! Err("qux"),
55//! Ok(4),
56//! Err("quux"),
57//! ];
58//!
59//! let mut sum = 0;
60//!
61//! for res in results {
62//! // Easily unwrap and handle results. Look at `IteratorExt::accumulate`
63//! // as that might fit your needs better with iterators:
64//! if let Some(n) = acc.handle(res) {
65//! sum += n;
66//! }
67//! }
68//!
69//! assert_eq!(sum, 7);
70//! assert_eq!(acc.len(), 5);
71//!
72//! // `into_vec` is one of the methods that terminate an accumulator properly.
73//! // Therefore, no panicking occurs:
74//! assert_eq!(acc.into_vec(), ["foo", "bar", "baz", "qux", "quux"]);
75//! ```
76//!
77//! Using an error that implements `FusedError`:
78//!
79//! ```
80//! use fused_error::{Accumulated, Accumulator};
81//!
82//! let mut acc = Accumulator::<Accumulated<&str>>::new();
83//! assert_eq!(acc.finish(), Ok(()));
84//!
85//! let mut acc = Accumulator::<Accumulated<&str>>::new();
86//! acc.push("foo");
87//! acc.push("bar");
88//! assert_eq!(acc.finish().unwrap_err(), ["foo", "bar"]);
89//!
90//! let mut acc = Accumulator::<Accumulated<&str>>::new();
91//! assert_eq!(acc.err_or(0), Ok(0));
92//!
93//! fn using_checkpoint() -> Result<(), Accumulated<&'static str>> {
94//! let mut acc = Accumulator::<Accumulated<&str>>::new();
95//! acc.push("baz");
96//! acc.push("qux");
97//!
98//! // This is shorthand for calling `finish` and making a new accumulator:
99//! let mut acc = acc.checkpoint()?;
100//!
101//! unreachable!()
102//! }
103//!
104//! assert_eq!(using_checkpoint().unwrap_err(), ["baz", "qux"])
105//! ```
106//!
107//! The following uses the `syn` feature to implement [`FusedError`] on
108//! [`syn::Error`]:
109//!
110//! ```
111//! # extern crate proc_macro;
112//! use fused_error::{Accumulator, FusedError};
113//! use proc_macro::TokenStream;
114//! use proc_macro2::TokenStream as TokenStream2;
115//! use syn::{AttributeArgs, DeriveInput, ItemFn};
116//!
117//! # const IGNORE: &str = stringify! {
118//! #[proc_macro_attribute]
119//! # };
120//! pub fn my_attribute(args: TokenStream, input: TokenStream) -> TokenStream {
121//! fn inner(args: TokenStream, input: TokenStream) -> syn::Result<TokenStream2> {
122//! let mut acc = Accumulator::<syn::Error>::new();
123//! // fn(TokenStream) -> syn::Result<AttributeArgs>
124//! let args = acc.handle(parse_args(args));
125//! let item = acc.handle(syn::parse::<ItemFn>(input));
126//!
127//! // Throw all of the collected parse errors, or continue:
128//! acc = acc.checkpoint()?;
129//!
130//! // SAFETY: any `None` value would short-circuit at the last
131//! // checkpoint.
132//! let mut args = unsafe { args.unwrap_unchecked() };
133//! let mut item = unsafe { item.unwrap_unchecked() };
134//!
135//! // fn(&AttributeArgs) -> syn::Result<()>
136//! acc.handle(validate_args(&args));
137//! // fn(&ItemFn) -> syn::Result<()>
138//! acc.handle(validate_item(&item));
139//!
140//! // Throw all of the validation parse errors, or continue:
141//! acc = acc.checkpoint()?;
142//!
143//! // Do multiple steps that can short-circuit:
144//! let tokens = acc.handle_in(|| {
145//! // fn(&mut AttributeArgs, &mut ItemFn) -> syn::Result<()>
146//! prepare(&mut args, &mut item)?;
147//! // fn(AttributeArgs, ItemFn) -> syn::Result<TokenStream2>
148//! expand(args, item)
149//! });
150//!
151//! // If this closure is called, we know `tokens` is `Some`:
152//! acc.err_or_else(|| unsafe { tokens.unwrap_unchecked() })
153//! }
154//!
155//! inner(args, input)
156//! .unwrap_or_else(syn::Error::into_compile_error)
157//! .into()
158//! }
159//! # fn parse_args(args: TokenStream) -> syn::Result<AttributeArgs> {
160//! # todo!()
161//! # }
162//! # fn validate_args(args: &AttributeArgs) -> syn::Result<()> {
163//! # todo!()
164//! # }
165//! #
166//! # fn validate_item(item: &ItemFn) -> syn::Result<()> {
167//! # todo!()
168//! # }
169//! #
170//! # fn prepare(args: &mut AttributeArgs, item: &mut ItemFn) -> syn::Result<()> {
171//! # todo!()
172//! # }
173//! #
174//! # fn expand(args: AttributeArgs, item: ItemFn) -> syn::Result<TokenStream2> {
175//! # todo!()
176//! # }
177//! ```
178//!
179//! # Method Overview
180//!
181//! Here is a basic outline of the accumulator methods at your disposal
182//! presented in the consecutive stages they'll most likely get called in.
183//!
184//! ## Creation
185//!
186//! To create an accumulator, use one of the following functions:
187//!
188//! * [`new`] to create an empty accumulator
189//! * [`from_vec`] if a vector of errors already exists
190//! * [`from_iter`](FromIterator::from_iter) (through the [`FromIterator`]
191//! trait)
192//!
193//! ## Accumulation
194//!
195//! All functions concerning the actual collection of errors are generic such
196//! that they accept any input of `IE`, which is any type that implements
197//! [`Into<E>`]. This is to reflect how the [`?`](std::ops::Try) operator
198//! already works.
199//!
200//! To collect an error into the accumulator, use one of the following:
201//!
202//! * [`push`] appends a single error
203//! * [`extend`](Extend::extend) (through the [`Extend`] trait)
204//!
205//! To append errors only if errors have already been collected, such as
206//! sub-diagnostics, use one of the following:
207//!
208//! * [`trace`] which conditionally calls [`push`]
209//! * [`trace_with`] to lazily [`trace`]
210//!
211//! To unwrap and handle results, use one of the following:
212//!
213//! * [`handle`] to handle any type that implements [`IntoResultParts`]
214//! * [`handle_in`] to collect from closures that use [`?`]
215//!
216//! ## Transformation
217//!
218//! Accumulators are, in "simplified" terms, protected vectors that validate
219//! their emptiness on drop with some utility methods. This, however, means that
220//! most operations should be thought of as iterators.
221//!
222//! For instance, a convoluted "map" method does not exist, because operating
223//! with iterator adapters like the following is encouraged instead:
224//!
225//! ```
226//! use fused_error::Accumulator;
227//!
228//! let mut acc = Accumulator::<&str>::new();
229//! acc.push("foo");
230//! acc.push("bar");
231//!
232//! let mut acc: Accumulator<String> = acc
233//! .into_iter()
234//! .enumerate()
235//! .map(|(i, s)| format!("{i}: {s}"))
236//! .collect();
237//!
238//! // Note that we can still use `&str` as an input, because &str
239//! // implements Into<String>.
240//! acc.push("2: baz");
241//!
242//! assert_eq!(acc.into_vec(), ["0: foo", "1: bar", "2: baz"])
243//! ```
244//!
245//! ## Termination
246//!
247//! **One of these methods *must* get called at the end of the accumulator's
248//! lifetime. It will panic otherwise.** For more information, look above at the
249//! "Panics" section.
250//!
251//! If the error type **does not** implement [`FusedError`], there are only
252//! two methods to properly handle an accumulator:
253//!
254//! * [`into_vec`] which returns a vector of the collected errors
255//! * [`ignore`] which is considered unsafe because it silently discards all
256//! errors
257//!
258//! However, if the error type **does** implement [`FusedError`], you can use
259//! one of the following:
260//!
261//! * [`finish`] which returns `Result<(), E>`
262//! * [`err`] which returns `Option<E>`
263//! * [`err_or`] which returns `Result<T, E>`
264//! * [`err_or_else`] which is the lazy version of [`err_or`]
265//!
266//! Then, if the error type **does** implement [`FusedError`], you can use
267//! [`checkpoint`] as shorthand for calling [`finish`] and then [`new`]. This
268//! isn't listed above, though, since it still returns another accumulator you
269//! have to handle.
270//!
271//! # Types
272//!
273//! In most instances, signatures concerning accumulators are purposefully
274//! generic. This design choice stems from the fact that Rust developers are
275//! already used to the question mark operator ([`?`]) and most error
276//! conversions happening implicitly. It's already enough technical debt to
277//! rethink errors and results as non-binary; there's no need to introduce
278//! conversion friction at call sites when the cost is just the odd 'turbofish'
279//! (`::<>`), often only when [`new`] is called.
280//!
281//! [`?`]: std::ops::Try
282//!
283//! [`new`]: Accumulator::new
284//! [`from_vec`]: Accumulator::from_vec
285//! [`len`]: Accumulator::len
286//! [`is_empty`]: Accumulator::is_empty
287//! [`iter`]: Accumulator::iter
288//! [`iter_mut`]: Accumulator::iter_mut
289//! [`push`]: Accumulator::push
290//! [`trace`]: Accumulator::trace
291//! [`trace_with`]: Accumulator::trace_with
292//! [`handle`]: Accumulator::handle
293//! [`handle_in`]: Accumulator::handle_in
294//! [`into_vec`]: Accumulator::into_vec
295//! [`ignore`]: Accumulator::ignore
296//! [`finish`]: Accumulator::finish
297//! [`err`]: Accumulator::err
298//! [`err_or`]: Accumulator::err_or
299//! [`err_or_else`]: Accumulator::err_or_else
300//! [`checkpoint`]: Accumulator::checkpoint
301
302use crate::{FusedError, IntoResultParts};
303
304use std::fmt::{self, Debug};
305
306mod raw;
307
308/// An error accumulator.
309///
310/// See the [module documentation] for details.
311///
312/// # Panics
313///
314/// **Accumulators panic on drop if not handled.** Be sure to read the "Panics"
315/// section in the [module documentation] for details.
316///
317/// [module documentation]: self
318#[must_use = "accumulators will panic on drop if not handled"]
319pub struct Accumulator<E> {
320 // This is `pub(crate)` for low-level access by result::raw::FusedResult.
321 pub(crate) inner: raw::Accumulator<E>,
322}
323
324impl<E> Accumulator<E> {
325 /// Constructs a new, empty accumulator.
326 ///
327 /// Because accumulators are so generalized, it can cause problems with
328 /// type inference. As such, `new` should probably get called with the
329 /// 'turbofish' syntax: `::<>`. This helps the inference algorithm
330 /// understand specifically which error type you're accumulating.
331 ///
332 /// # Examples
333 ///
334 /// ```
335 /// use fused_error::Accumulator;
336 ///
337 /// let mut acc = Accumulator::<&str>::new();
338 ///
339 /// // ...
340 /// # unsafe { acc.ignore() };
341 /// ```
342 ///
343 /// *Note:* You may have noticed the trailing ellipsis comment. This is
344 /// because, as outlined in the documentation for [`Accumulator<E>`], any
345 /// unhandled accumulator will panic on drop. That comment in any example
346 /// is meant to signify the accumulator getting handled at a later point in
347 /// the program.
348 #[inline]
349 pub fn new() -> Self {
350 let inner = raw::Accumulator::new();
351 Accumulator { inner }
352 }
353
354 /// Constructs a new accumulator from a vector of errors.
355 ///
356 /// Unlike [`new`](Accumulator::new), it's very unlikely that the type
357 /// inference algorithm can't guess the error type you're meaning to
358 /// accumulate.
359 ///
360 /// # Examples
361 ///
362 /// ```
363 /// use fused_error::Accumulator;
364 ///
365 /// let vec = vec!["foo", "bar"];
366 /// let mut acc = Accumulator::from_vec(vec);
367 ///
368 /// // ...
369 /// # unsafe { acc.ignore() };
370 /// ```
371 #[inline]
372 pub fn from_vec(vec: Vec<E>) -> Self {
373 let inner = raw::Accumulator::from_vec(vec);
374 Accumulator { inner }
375 }
376
377 /// Returns the number of errors in the accumulator.
378 ///
379 /// # Examples
380 ///
381 /// ```
382 /// use fused_error::Accumulator;
383 ///
384 /// let vec = vec!["foo", "bar"];
385 /// let mut acc = Accumulator::from_vec(vec);
386 /// assert_eq!(acc.len(), 2);
387 ///
388 /// // ...
389 /// # unsafe { acc.ignore() };
390 /// ```
391 #[must_use]
392 #[inline]
393 pub fn len(&self) -> usize {
394 self.inner.as_ref().len()
395 }
396
397 /// Returns `true` if the accumulator contains no errors.
398 ///
399 /// # Examples
400 ///
401 /// ```
402 /// use fused_error::Accumulator;
403 ///
404 /// let mut acc = Accumulator::<&str>::new();
405 /// assert!(acc.is_empty());
406 ///
407 /// acc.push("foo");
408 /// assert!(!acc.is_empty());
409 ///
410 /// // ...
411 /// # unsafe { acc.ignore() };
412 /// ```
413 #[must_use]
414 #[inline]
415 pub fn is_empty(&self) -> bool {
416 self.len() == 0
417 }
418
419 /// Returns an iterator over the accumulated errors.
420 ///
421 /// # Examples
422 ///
423 /// ```
424 /// use fused_error::Accumulator;
425 ///
426 /// let vec = vec!["foo", "bar"];
427 /// let mut acc = Accumulator::from_vec(vec);
428 /// let mut iter = acc.iter();
429 ///
430 /// assert_eq!(iter.next(), Some(&"foo"));
431 /// assert_eq!(iter.next(), Some(&"bar"));
432 /// assert_eq!(iter.next(), None);
433 ///
434 /// // ...
435 /// # unsafe { acc.ignore() };
436 /// ```
437 #[inline]
438 pub fn iter(&self) -> Iter<'_, E> {
439 self.inner.as_ref().iter()
440 }
441
442 /// Returns an iterator of the accumulated errors that allows modifying each
443 /// value.
444 ///
445 /// # Examples
446 ///
447 /// ```
448 /// use fused_error::Accumulator;
449 ///
450 /// let vec = vec![" foo\n", " bar\n"];
451 /// let mut acc = Accumulator::from_vec(vec);
452 ///
453 /// for err in acc.iter_mut() {
454 /// *err = err.trim()
455 /// }
456 ///
457 /// assert_eq!(acc.into_vec(), ["foo", "bar"]);
458 /// ```
459 #[inline]
460 pub fn iter_mut(&mut self) -> IterMut<'_, E> {
461 self.inner.as_mut().iter_mut()
462 }
463
464 /// Pushes an error into the accumulator.
465 ///
466 /// To emulate the [question mark operator's](std::ops::Try) behavior of
467 /// performing the necessary conversions behind the scenes, `push` accepts
468 /// any type that can get converted into the error type the accumulator is
469 /// collecting.
470 ///
471 /// # Panics
472 ///
473 /// Panics if the new capacity of the accumulator exceeds `isize::MAX`
474 /// bytes.
475 ///
476 /// # Examples
477 ///
478 /// ```
479 /// use fused_error::Accumulator;
480 ///
481 /// // We're collecting `String` errors, so `push` can except anything that
482 /// // implements `Into<String>`.
483 /// let mut acc = Accumulator::<String>::new();
484 ///
485 /// acc.push(String::from("foo"));
486 /// acc.push("bar");
487 ///
488 /// assert_eq!(acc.into_vec(), ["foo", "bar"]);
489 /// ```
490 ///
491 /// If you have to push an `Option<E>`, consider taking advantage of the
492 /// [`Extend`] implementation and the fact that `Option` implements
493 /// [`IntoIterator`] instead:
494 ///
495 /// ```
496 /// use fused_error::Accumulator;
497 ///
498 /// let mut acc = Accumulator::<String>::new();
499 ///
500 /// let err1 = Some("foo");
501 /// let err2: Option<String> = None;
502 /// let err3 = Some("baz");
503 /// let err4 = Some(String::from("qux"));
504 ///
505 /// // This is what you might be inclined to do:
506 /// if let Some(e) = err1 {
507 /// acc.push(e);
508 /// }
509 ///
510 /// // Instead, you can just do this:
511 /// acc.extend(err2);
512 /// acc.extend(err3);
513 /// acc.extend(err4);
514 ///
515 /// assert_eq!(acc.into_vec(), ["foo", "baz", "qux"]);
516 /// ```
517 #[inline]
518 #[track_caller]
519 pub fn push<IE>(&mut self, err: IE)
520 where
521 IE: Into<E>,
522 {
523 self.inner.push(err.into());
524 }
525
526 /// Pushes an error only if the accumulator **is not** empty.
527 ///
528 /// Arguments passed to `trace` are eagerly evaluated; if you are passing
529 /// the result of a function call, it is recommended to use
530 /// [`trace_with`](Accumulator::trace_with), which is lazily evaluated.
531 ///
532 /// To emulate the [question mark operator's](std::ops::Try) behavior of
533 /// performing the necessary conversions behind the scenes, `trace` accepts
534 /// any type that can get converted into the error type the accumulator is
535 /// collecting.
536 ///
537 /// # Panics
538 ///
539 /// Panics if the new capacity of the accumulator exceeds `isize::MAX`
540 /// bytes.
541 ///
542 /// # Examples
543 ///
544 /// ```
545 /// use fused_error::Accumulator;
546 ///
547 /// let mut acc = Accumulator::<String>::new();
548 /// acc.trace(String::from("foo"));
549 /// assert!(acc.is_empty());
550 ///
551 /// acc.push("bar");
552 /// acc.trace("baz");
553 /// assert_eq!(acc.into_vec(), ["bar", "baz"]);
554 /// ```
555 #[inline]
556 #[track_caller]
557 pub fn trace<IE>(&mut self, err: IE)
558 where
559 IE: Into<E>,
560 {
561 if !self.is_empty() {
562 self.push(err);
563 }
564 }
565
566 /// Emulates [`extend`] but only if the accumulator **is not** empty.
567 ///
568 /// To emulate the [question mark operator's](std::ops::Try) behavior of
569 /// performing the necessary conversions behind the scenes, `trace_iter`
570 /// accepts any iterator whose item's type can get converted into the error
571 /// type the accumulator is collecting.
572 ///
573 /// # Panics
574 ///
575 /// Panics if the new capacity of the accumulator exceeds `isize::MAX`
576 /// bytes.
577 ///
578 /// # Examples
579 ///
580 /// ```
581 /// use fused_error::Accumulator;
582 ///
583 /// let mut acc = Accumulator::<&str>::new();
584 ///
585 /// acc.trace_iter(["foo", "bar"]);
586 /// assert!(acc.is_empty());
587 ///
588 /// acc.push("baz");
589 /// acc.trace_iter(["qux", "quux"]);
590 ///
591 /// assert_eq!(acc.into_vec(), ["baz", "qux", "quux"]);
592 /// ```
593 ///
594 /// Similar to using [`extend`], consider using `trace_iter`:
595 ///
596 /// ```
597 /// use fused_error::Accumulator;
598 ///
599 /// let mut acc = Accumulator::<&str>::new();
600 /// acc.push("foo");
601 ///
602 /// let trace1 = Some("bar");
603 /// let trace2: Option<&str> = None;
604 /// let trace3 = Some("qux");
605 ///
606 /// // This is what you might be inclined to do:
607 /// if let Some(e) = trace1 {
608 /// acc.trace(e);
609 /// }
610 ///
611 /// // Instead, you can just do this:
612 /// acc.trace_iter(trace2);
613 /// acc.trace_iter(trace3);
614 ///
615 /// assert_eq!(acc.into_vec(), ["foo", "bar", "qux"]);
616 /// ```
617 ///
618 /// [`extend`]: Extend::extend
619 #[inline]
620 #[track_caller]
621 pub fn trace_iter<IE, I>(&mut self, iter: I)
622 where
623 IE: Into<E>,
624 I: IntoIterator<Item = IE>,
625 {
626 if !self.is_empty() {
627 iter.into_iter().for_each(|err| self.push(err));
628 }
629 }
630
631 /// Calls `f` if there are any errors in the accumulator and collects the
632 /// returned error.
633 ///
634 /// To emulate the [question mark operator's](std::ops::Try) behavior of
635 /// performing the necessary conversions behind the scenes, `f` can return
636 /// any type that can get converted into the error type the accumulator is
637 /// collecting.
638 ///
639 /// # Panics
640 ///
641 /// Panics if the new capacity of the accumulator exceeds `isize::MAX`
642 /// bytes.
643 ///
644 /// # Examples
645 ///
646 /// ```
647 /// use fused_error::Accumulator;
648 ///
649 /// let mut acc = Accumulator::<String>::new();
650 /// acc.trace_with(||"foo");
651 /// assert!(acc.is_empty());
652 ///
653 /// acc.push("bar");
654 /// acc.trace_with(|| "baz");
655 /// assert_eq!(acc.into_vec(), ["bar", "baz"]);
656 /// ```
657 #[inline]
658 pub fn trace_with<IE, F>(&mut self, f: F)
659 where
660 IE: Into<E>,
661 F: FnOnce() -> IE,
662 {
663 if !self.is_empty() {
664 self.push(f());
665 }
666 }
667
668 /// Handles a result, collecting the error and returning any "ok" value, if
669 /// present.
670 ///
671 /// If you are working with iterators, whenever possible, prefer
672 /// [`IteratorExt::accumulate`](crate::IteratorExt::accumulate).
673 ///
674 /// This method is incredibly versatile. A result **does not** have to
675 /// necessarily be a [`Result<T, IE>`](Result) (where `IE` is any type that
676 /// implements [`Into<E>`]). Instead, it just has to implement
677 /// [`IntoResultParts`].
678 ///
679 /// # Panics
680 ///
681 /// Panics if the new capacity of the accumulator exceeds `isize::MAX`
682 /// bytes.
683 ///
684 /// # Examples
685 ///
686 /// ```
687 /// use std::num::ParseIntError;
688 /// use fused_error::Accumulator;
689 ///
690 /// let mut acc = Accumulator::<ParseIntError>::new();
691 /// assert_eq!(acc.handle("1".parse::<i32>()), Some(1));
692 ///
693 /// assert_eq!(acc.handle("invalid".parse::<i32>()), None);
694 /// assert_eq!(acc.len(), 1);
695 ///
696 /// // ...
697 /// # unsafe { acc.ignore() };
698 /// ```
699 #[inline]
700 pub fn handle<R>(&mut self, res: R) -> Option<<R as IntoResultParts>::Ok>
701 where
702 R: IntoResultParts,
703 <R as IntoResultParts>::Err: Into<E>,
704 {
705 let (ok, err) = res.into_result_parts();
706 if let Some(err) = err {
707 self.push(err.into());
708 }
709 ok
710 }
711
712 /// Calls `f`, returning the successful value as `Some`, or collecting the
713 /// error and returning `None`.
714 ///
715 /// Because the closure's return type is a result, you can use the
716 /// question mark operator inside of it: [`?`](std::ops::Try).
717 ///
718 /// Unlike `handle`, this method only accepts results instead of any type
719 /// that implements [`IntoResultParts`]. This is because
720 /// [`Try`](std::ops::Try) is not on stable and developer ergonomics
721 /// would suffer immensely otherwise.
722 ///
723 /// # Panics
724 ///
725 /// Panics if the new capacity of the accumulator exceeds `isize::MAX`
726 /// bytes.
727 ///
728 /// # Examples
729 ///
730 /// ```
731 /// use std::num::ParseIntError;
732 /// use fused_error::Accumulator;
733 ///
734 /// let mut acc = Accumulator::<ParseIntError>::new();
735 ///
736 /// let sum = acc.handle_in(|| {
737 /// // All of these inputs are valid. No errors expected.
738 /// let a: i32 = "1".parse()?;
739 /// let b: i32 = "2".parse()?;
740 /// let c: i32 = "3".parse()?;
741 /// Ok(a + b + c)
742 /// });
743 /// assert_eq!(sum, Some(6));
744 /// assert!(acc.is_empty());
745 ///
746 /// let product = acc.handle_in(|| {
747 /// // All inputs are invalid. Because of how `?` works, `a` will
748 /// // short-circuit the closure.
749 /// let a: i32 = "foo".parse()?;
750 /// let b: i32 = "bar".parse()?;
751 /// let c: i32 = "baz".parse()?;
752 /// Ok(a * b * c)
753 /// });
754 /// assert!(product.is_none());
755 /// assert_eq!(acc.len(), 1);
756 ///
757 /// // ...
758 /// # unsafe { acc.ignore() };
759 /// ```
760 #[inline]
761 pub fn handle_in<T, F>(&mut self, f: F) -> Option<T>
762 where
763 F: FnOnce() -> Result<T, E>,
764 {
765 self.handle(f())
766 }
767
768 /// Extracts the vector of collected errors.
769 ///
770 /// Calling this method ensures the accumulator **will not** panic on drop.
771 ///
772 /// # Examples
773 ///
774 /// ```
775 /// use fused_error::Accumulator;
776 ///
777 /// let mut acc = Accumulator::<&str>::new();
778 /// acc.push("foo");
779 /// acc.push("bar");
780 ///
781 /// assert_eq!(acc.into_vec(), ["foo", "bar"]);
782 /// ```
783 #[must_use = "if you want to ignore the accumulator, consider `.ignore()` instead"]
784 #[inline]
785 pub fn into_vec(mut self) -> Vec<E> {
786 // SAFETY: accumulator is dropped immediately after
787 unsafe { self.inner.take() }
788 }
789
790 /// Handles this accumulator, discarding all errors.
791 ///
792 /// Calling this method ensures the accumulator **will not** panic on drop.
793 ///
794 /// # Safety
795 ///
796 /// It is considered semantically unsafe to discard errors, especially an
797 /// accumulator due to the volume of errors they can store.
798 ///
799 /// # Examples
800 ///
801 /// ```
802 /// use fused_error::Accumulator;
803 ///
804 /// let mut acc = Accumulator::<&str>::new();
805 /// acc.push("foo");
806 /// acc.push("bar");
807 ///
808 /// // TODO: properly handle errors
809 /// unsafe { acc.ignore() };
810 /// ```
811 #[inline]
812 pub unsafe fn ignore(mut self) {
813 // SAFETY: accumulator is dropped immediately after and discarding is
814 // ensured to be semantically safe by the caller.
815 self.inner.ignore();
816 }
817
818 /// Returns `true` if further operations on the accumulator are safe.
819 #[must_use]
820 #[inline]
821 pub(crate) fn is_handled(&self) -> bool {
822 self.inner.is_handled()
823 }
824}
825
826impl<E> Accumulator<E>
827where
828 E: FusedError,
829{
830 /// Returns `Ok(())` if the accumulator is empty, otherwise reduces the
831 /// [`FusedError`] type into `Err(E)`.
832 ///
833 /// Calling this method ensures the accumulator **will not** panic on drop.
834 ///
835 /// # Examples
836 ///
837 /// ```
838 /// use fused_error::{Accumulated, Accumulator};
839 ///
840 /// type Error = Accumulated<String>;
841 ///
842 /// let mut acc = Accumulator::<Error>::new();
843 /// assert_eq!(acc.finish(), Ok(()));
844 ///
845 /// let mut acc = Accumulator::<Error>::new();
846 /// acc.push("foo".to_string());
847 /// acc.push("bar".to_string());
848 /// assert_eq!(acc.finish().unwrap_err(), ["foo", "bar"]);
849 /// ```
850 pub fn finish(self) -> Result<(), E> {
851 self.err_or(())
852 }
853
854 /// Returns `None` if the accumulator is empty, otherwise reduces the
855 /// [`FusedError`] type into `Some(E)`.
856 ///
857 /// Calling this method ensures the accumulator **will not** panic on drop.
858 ///
859 /// # Examples
860 ///
861 /// ```
862 /// use fused_error::{Accumulated, Accumulator};
863 ///
864 /// let mut acc = Accumulator::<Accumulated<&str>>::new();
865 /// assert_eq!(acc.err(), None);
866 ///
867 /// let mut acc = Accumulator::<Accumulated<&str>>::new();
868 /// acc.push("foo");
869 /// acc.push("bar");
870 /// assert_eq!(acc.err().unwrap(), ["foo", "bar"]);
871 /// ```
872 #[must_use]
873 #[inline]
874 pub fn err(mut self) -> Option<E> {
875 // SAFETY: accumulator is dropped immediately after
876 unsafe { self.inner.reduce() }
877 }
878
879 /// Returns `Ok(ok)` if the accumulator is empty, otherwise reduces the
880 /// [`FusedError`] type into `Err(E)`.
881 ///
882 /// Calling this method ensures the accumulator **will not** panic on drop.
883 ///
884 /// Arguments passed to `err_or` are eagerly evaluated; if you are passing
885 /// the result of a function call, it is recommended to use
886 /// [`err_or_else`](Accumulator::err_or_else), which is lazily evaluated.
887 ///
888 /// # Examples
889 ///
890 /// ```
891 /// use fused_error::{Accumulated, Accumulator};
892 ///
893 /// let mut acc = Accumulator::<Accumulated<&str>>::new();
894 /// assert_eq!(acc.err_or(0), Ok(0));
895 ///
896 /// let mut acc = Accumulator::<Accumulated<&str>>::new();
897 /// acc.push("foo");
898 /// acc.push("bar");
899 /// assert_eq!(acc.err_or(0).unwrap_err(), ["foo", "bar"]);
900 /// ```
901 #[inline]
902 pub fn err_or<T>(self, ok: T) -> Result<T, E> {
903 match self.err() {
904 None => Ok(ok),
905 Some(err) => Err(err),
906 }
907 }
908
909 /// Returns `Ok(f())` if the accumulator is empty, otherwise reduces the
910 /// [`FusedError`] type into `Err(E)`.
911 ///
912 /// Calling this method ensures the accumulator **will not** panic on drop.
913 ///
914 /// # Examples
915 ///
916 /// ```
917 /// use fused_error::{Accumulated, Accumulator};
918 ///
919 /// let mut acc = Accumulator::<Accumulated<&str>>::new();
920 /// assert_eq!(acc.err_or_else(|| 0), Ok(0));
921 ///
922 /// let mut acc = Accumulator::<Accumulated<&str>>::new();
923 /// acc.push("foo");
924 /// acc.push("bar");
925 /// assert_eq!(acc.err_or_else(|| 0).unwrap_err(), ["foo", "bar"]);
926 /// ```
927 #[inline]
928 pub fn err_or_else<T, F>(self, f: F) -> Result<T, E>
929 where
930 F: FnOnce() -> T,
931 {
932 match self.err() {
933 None => Ok(f()),
934 Some(err) => Err(err),
935 }
936 }
937
938 /// Returns `Ok(Accumulator<E>)` if the accumulator is empty, otherwise
939 /// reduces the [`FusedError`] type into `Err(E)`.
940 ///
941 /// This method is particularly useful for short-circuiting with the
942 /// [`?`](std::ops::Try) operator.
943 ///
944 /// # Examples
945 ///
946 /// ```should_panic
947 /// use fused_error::{Accumulated, Accumulator};
948 ///
949 /// # fn main() -> Result<(), Accumulated<String>> {
950 /// let mut acc = Accumulator::<Accumulated<String>>::new();
951 /// let a = acc.handle("200".parse::<u8>().map_err(|e| e.to_string()));
952 /// let b = acc.handle("100".parse::<u8>().map_err(|e| e.to_string()));
953 ///
954 /// acc = acc.checkpoint()?;
955 ///
956 /// // SAFETY: We know all past "handle" calls have returned as successes.
957 /// let a: u8 = unsafe { a.unwrap_unchecked() };
958 /// let b: u8 = unsafe { b.unwrap_unchecked() };
959 ///
960 /// // These will both error:
961 /// let sum = acc.handle(a.checked_add(b).ok_or("addition overflow".to_string()));
962 /// let product = acc.handle(a.checked_mul(b).ok_or("multiplication overflow".to_string()));
963 ///
964 /// // This will short-circuit with the following error:
965 /// //
966 /// // Error {
967 /// // messages: [
968 /// // "addition overflow",
969 /// // "multiplication overflow",
970 /// // ],
971 /// // }
972 /// acc = acc.checkpoint()?;
973 ///
974 /// // ...
975 /// # unsafe { acc.ignore(); }
976 /// # Ok(())
977 /// # }
978 /// ```
979 #[inline]
980 pub fn checkpoint(self) -> Result<Self, E> {
981 self.err_or_else(Accumulator::new)
982 }
983}
984
985////////////////////////////////////////////////////////////////////////////////
986// Trait implementations
987////////////////////////////////////////////////////////////////////////////////
988
989impl<E> Debug for Accumulator<E>
990where
991 E: Debug,
992{
993 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
994 f.debug_tuple("Accumulator").field(&self.inner).finish()
995 }
996}
997
998impl<E> Default for Accumulator<E> {
999 #[inline]
1000 fn default() -> Self {
1001 Accumulator::new()
1002 }
1003}
1004
1005////////////////////////////////////////////////////////////////////////////////
1006// Iterator traits
1007////////////////////////////////////////////////////////////////////////////////
1008
1009impl<E> FromIterator<E> for Accumulator<E> {
1010 #[inline]
1011 fn from_iter<T: IntoIterator<Item = E>>(iter: T) -> Self {
1012 let vec: Vec<E> = iter.into_iter().collect();
1013 Accumulator::from_vec(vec)
1014 }
1015}
1016
1017impl<E, IE> Extend<IE> for Accumulator<E>
1018where
1019 IE: Into<E>,
1020{
1021 #[inline]
1022 fn extend<T: IntoIterator<Item = IE>>(&mut self, iter: T) {
1023 self.inner.as_mut().extend(iter.into_iter().map(Into::into));
1024 }
1025}
1026
1027impl<E> IntoIterator for Accumulator<E> {
1028 type Item = E;
1029 type IntoIter = IntoIter<E>;
1030
1031 /// Calling this method ensures the accumulator **will not** panic on drop.
1032 #[inline]
1033 fn into_iter(mut self) -> Self::IntoIter {
1034 // SAFETY: accumulator is dropped immediately after
1035 unsafe { self.inner.take() }.into_iter()
1036 }
1037}
1038
1039impl<'a, E> IntoIterator for &'a Accumulator<E> {
1040 type Item = &'a E;
1041 type IntoIter = Iter<'a, E>;
1042
1043 #[inline]
1044 fn into_iter(self) -> Self::IntoIter {
1045 self.iter()
1046 }
1047}
1048
1049impl<'a, E> IntoIterator for &'a mut Accumulator<E> {
1050 type Item = &'a mut E;
1051 type IntoIter = IterMut<'a, E>;
1052
1053 #[inline]
1054 fn into_iter(self) -> Self::IntoIter {
1055 self.iter_mut()
1056 }
1057}
1058
1059////////////////////////////////////////////////////////////////////////////////
1060// Iterator type aliases
1061////////////////////////////////////////////////////////////////////////////////
1062
1063/// Immutable error iterator.
1064///
1065/// This is created by the [`iter`](Accumulator::iter) method on
1066/// [`accumulators`](Accumulator).
1067pub type Iter<'a, T> = std::slice::Iter<'a, T>;
1068
1069/// Mutable error iterator.
1070///
1071/// This is created by the [`iter_mut`](Accumulator::iter_mut) method on
1072/// [`accumulators`](Accumulator).
1073pub type IterMut<'a, T> = std::slice::IterMut<'a, T>;
1074
1075/// An iterator that moves out of an accumulator.
1076///
1077/// This is created by the `into_iter` method on [`accumulators`](Accumulator)
1078/// (provided by the [`IntoIterator`] trait).
1079pub type IntoIter<T> = std::vec::IntoIter<T>;