error_graph/
strategy.rs

1//! Strategies a caller may use to collect errors from subfunctions
2//!
3//! Currently, the strategies contained in this module are:
4//!
5//! -   [DontCare]: The caller will ignore any non-fatal errors in subfunction.
6//!     [WriteErrorList::push] is effectively a no-op.
7//!
8//! -   [ErrorOccurred]: Keeps track of a single boolean about whether an error occurred or not.
9//!     [WriteErrorList::push] essentially just sets a flag.
10//!
11//! -   [Sublist]: A full-fledged list of all non-fatal errors in subfunction. Will be mapped to
12//!     the caller's error type with a map function and pushed into the caller's error list.
13use {
14    crate::{private, ErrorList, WriteErrorList},
15    std::ops::{Deref, DerefMut},
16};
17
18/// A sublist that maps a list of errors into a parent error type
19///
20/// When an object of this type is dropped, it will call the given `MapFn` object with a
21/// [`ErrorList<E>`][ErrorList] containing all the errors that were pushed into it. The map
22/// function will be used to map that error list to a single `ParentErr` object which will then
23/// be pushed onto the parent's error list.
24///
25/// This type implements [DerefMut] to an [ErrorList], so it can basically be thought of as
26/// an [ErrorList] with a fancy destructor.
27pub struct Sublist<'a, E, MapFn, Parent, ParentErr>
28where
29    MapFn: FnOnce(ErrorList<E>) -> ParentErr,
30    Parent: WriteErrorList<ParentErr>,
31{
32    list: ErrorList<E>,
33    map_fn_and_parent: Option<(MapFn, &'a mut Parent)>,
34}
35
36impl<'a, E, MapFn, Parent, ParentErr> Sublist<'a, E, MapFn, Parent, ParentErr>
37where
38    MapFn: FnOnce(ErrorList<E>) -> ParentErr,
39    Parent: WriteErrorList<ParentErr>,
40{
41    /// Create a new sublist that maps a list of subfunction errors to the parent error
42    ///
43    /// `map_fn` is a function that accepts an `ErrorList<E>` and returns a `ParentErr`, which
44    /// is then pushed into the parent's error list.
45    ///
46    /// It is recommended use [WriteErrorList::sublist] instead of this.
47    pub fn new(map_fn: MapFn, parent: &'a mut Parent) -> Self {
48        Self {
49            list: ErrorList::default(),
50            map_fn_and_parent: Some((map_fn, parent)),
51        }
52    }
53}
54
55impl<'a, E, MapFn, Parent, ParentErr> Drop for Sublist<'a, E, MapFn, Parent, ParentErr>
56where
57    MapFn: FnOnce(ErrorList<E>) -> ParentErr,
58    Parent: WriteErrorList<ParentErr>,
59{
60    fn drop(&mut self) {
61        if !self.list.is_empty() {
62            let list = std::mem::take(&mut self.list);
63            let (map_fn, parent) = self.map_fn_and_parent.take().unwrap();
64            let parent_error = map_fn(list);
65            parent.push(parent_error);
66        }
67    }
68}
69
70impl<'a, E, MapFn, Parent, ParentErr> Deref for Sublist<'a, E, MapFn, Parent, ParentErr>
71where
72    MapFn: FnOnce(ErrorList<E>) -> ParentErr,
73    Parent: WriteErrorList<ParentErr>,
74{
75    type Target = ErrorList<E>;
76    fn deref(&self) -> &Self::Target {
77        &self.list
78    }
79}
80
81impl<'a, E, MapFn, Parent, ParentErr> DerefMut for Sublist<'a, E, MapFn, Parent, ParentErr>
82where
83    MapFn: FnOnce(ErrorList<E>) -> ParentErr,
84    Parent: WriteErrorList<ParentErr>,
85{
86    fn deref_mut(&mut self) -> &mut Self::Target {
87        &mut self.list
88    }
89}
90
91impl<'a, E, MapFn, Parent, ParentErr> private::Sealed<E>
92    for Sublist<'a, E, MapFn, Parent, ParentErr>
93where
94    MapFn: FnOnce(ErrorList<E>) -> ParentErr,
95    Parent: WriteErrorList<ParentErr>,
96{
97}
98
99impl<'a, E, MapFn, Parent, ParentErr> WriteErrorList<E> for Sublist<'a, E, MapFn, Parent, ParentErr>
100where
101    MapFn: FnOnce(ErrorList<E>) -> ParentErr,
102    Parent: WriteErrorList<ParentErr>,
103{
104    fn push(&mut self, error: E) {
105        self.list.push(error)
106    }
107    fn subwriter<'sub, SubMapFn, SubErr: 'sub>(
108        &'sub mut self,
109        map_fn: SubMapFn,
110    ) -> impl WriteErrorList<SubErr> + 'sub
111    where
112        SubMapFn: FnOnce(ErrorList<SubErr>) -> E + 'sub,
113    {
114        self.sublist(map_fn)
115    }
116}
117
118/// An error list writer that ignores errors
119///
120/// Any call to [WriteErrorList::push] does nothing but drop the given error.
121pub struct DontCare;
122
123impl<E> private::Sealed<E> for DontCare {}
124
125impl<E> WriteErrorList<E> for DontCare {
126    fn push(&mut self, _error: E) {}
127    fn subwriter<'sub, SubMapFn, SubErr: 'sub>(
128        &'sub mut self,
129        _map_fn: SubMapFn,
130    ) -> impl WriteErrorList<SubErr> + 'sub
131    where
132        SubMapFn: FnOnce(ErrorList<SubErr>) -> E + 'sub,
133    {
134        DontCare
135    }
136}
137
138/// An error list writer that only notes that an error occurred
139///
140/// [ErrorOccurred::as_bool] will return `true` if the subfunction encountered a non-fatal error
141/// `false` otherwise
142#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
143pub struct ErrorOccurred(bool);
144
145impl ErrorOccurred {
146    pub fn as_bool(&self) -> bool {
147        self.0
148    }
149}
150
151impl<E> private::Sealed<E> for ErrorOccurred {}
152
153impl<E> WriteErrorList<E> for ErrorOccurred {
154    fn push(&mut self, _error: E) {
155        self.0 = true;
156    }
157    fn subwriter<'sub, SubMapFn, SubErr: 'sub>(
158        &'sub mut self,
159        _map_fn: SubMapFn,
160    ) -> impl WriteErrorList<SubErr> + 'sub
161    where
162        SubMapFn: FnOnce(ErrorList<SubErr>) -> E + 'sub,
163    {
164        self
165    }
166}