Skip to main content

problemo/problems/
problems.rs

1use super::super::{error::*, implementation::*, problem::*, receiver::*};
2
3use std::{any::*, collections::*, error::Error, fmt, slice::*};
4
5/// Prefix for [Problems] [Display](fmt::Display).
6pub const PROBLEMS_DISPLAY_PREFIX: &str = "• ";
7
8//
9// Problems
10//
11
12/// A simple wrapper for a vector of [Problem].
13///
14/// Useful for accumulating problems, as it implements [ProblemReceiver]. By default it will
15/// swallow all problems into its vector. However, it can also be configured to fail fast on
16/// specific error types by calling [handle_type_as_critical](Problems::handle_type_as_critical)
17/// before use.
18///
19/// After use, you may want to call [check](Problems::check) to fail if there were problems.
20#[derive(Debug, Default)]
21pub struct Problems {
22    /// Problems.
23    pub problems: Vector<Problem>,
24
25    /// Critical error type IDs.
26    pub critical_error_types: HashSet<TypeId>,
27}
28
29impl Problems {
30    /// Constructor.
31    pub fn with_capacity(capacity: usize) -> Self {
32        Vector::with_capacity(capacity).into()
33    }
34
35    /// True if there are no problems.
36    pub fn is_empty(&self) -> bool {
37        self.problems.is_empty()
38    }
39
40    /// The number of problems.
41    pub fn count(&self) -> usize {
42        self.problems.len()
43    }
44
45    /// Iterate problems.
46    pub fn iter(&self) -> Iter<'_, Problem> {
47        self.into_iter()
48    }
49
50    /// Marks a top error type as critical.
51    pub fn handle_type_as_critical<ErrorT>(&mut self)
52    where
53        ErrorT: Any + Error,
54    {
55        self.critical_error_types.insert(TypeId::of::<ErrorT>());
56    }
57
58    /// True if the problem's top error is critical.
59    pub fn is_critical(&self, problem: &Problem) -> bool {
60        problem
61            .top()
62            .map(|cause| self.is_error_critical(&cause.error))
63            .unwrap_or(false)
64    }
65
66    /// True if the error is critical.
67    pub fn is_error_critical(&self, error: &CapturedError) -> bool {
68        self.critical_error_types.contains(&error.type_id())
69    }
70
71    /// Add a problem.
72    pub fn add<ProblemT>(&mut self, problem: ProblemT)
73    where
74        ProblemT: Into<Problem>,
75    {
76        self.problems.push(problem.into())
77    }
78
79    /// Fails with self if there is at least one problem.
80    pub fn check(self) -> Result<(), Self> {
81        if self.is_empty() { Ok(()) } else { Err(self) }
82    }
83
84    /// Iterate unique problems.
85    ///
86    /// Equality is tested for using the first [ErrorEquality] or [ErrorComparison] found in each
87    /// problem.
88    pub fn iter_unique<'this>(&'this self) -> impl Iterator<Item = &'this Problem> {
89        iter_unique_problems(&self.problems)
90    }
91}
92
93impl IntoProblem for Problems {
94    fn into_problem(self) -> Problem {
95        self.into()
96    }
97}
98
99impl ProblemReceiver for Problems {
100    fn give(&mut self, problem: Problem) -> Result<(), Problem> {
101        // Fail fast if critical
102        match self.is_critical(&problem) {
103            // Fail fast if critical
104            true => Err(problem),
105
106            // Otherwise, swallow
107            false => {
108                self.add(problem);
109                Ok(())
110            }
111        }
112    }
113}
114
115impl fmt::Display for Problems {
116    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
117        let mut iterator = self.into_iter().peekable();
118        while let Some(problem) = iterator.next() {
119            write!(formatter, "{}{}", PROBLEMS_DISPLAY_PREFIX, problem)?;
120            if iterator.peek().is_some() {
121                writeln!(formatter)?;
122            }
123        }
124        Ok(())
125    }
126}
127
128impl From<Problems> for Problem {
129    fn from(mut problems: Problems) -> Self {
130        if !problems.is_empty() {
131            problems.problems.remove(0)
132        } else {
133            Problem::default()
134        }
135    }
136}
137
138impl From<Vector<Problem>> for Problems {
139    fn from(problems: Vector<Problem>) -> Self {
140        Self {
141            problems,
142            critical_error_types: Default::default(),
143        }
144    }
145}
146
147impl<'this> IntoIterator for &'this Problems {
148    type Item = &'this Problem;
149    type IntoIter = Iter<'this, Problem>;
150
151    fn into_iter(self) -> Self::IntoIter {
152        self.problems.iter()
153    }
154}
155
156impl FromIterator<Problem> for Problems {
157    fn from_iter<IntoIteratorT>(iterator: IntoIteratorT) -> Self
158    where
159        IntoIteratorT: IntoIterator<Item = Problem>,
160    {
161        iterator.into_iter().collect::<Vector<_>>().into()
162    }
163}
164
165impl<ErrorT> FromIterator<ErrorT> for Problems
166where
167    ErrorT: 'static + Error + Send + Sync,
168{
169    fn from_iter<IntoIteratorT>(iterator: IntoIteratorT) -> Self
170    where
171        IntoIteratorT: IntoIterator<Item = ErrorT>,
172    {
173        Self::from_iter(iterator.into_iter().map(Problem::from))
174    }
175}