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>;