molframe_core/diagnostic/findings.rs
1//! A finished set of findings, as the error an operation returns.
2
3use super::code::Code;
4use super::finding::Diagnostic;
5use std::fmt;
6
7/// The findings an operation produced, as a finished set.
8///
9/// [`Diagnostics`](super::Diagnostics) accumulates and
10/// [`Diagnostics::finish`](super::Diagnostics::finish) imposes the order; this is
11/// what comes out the other side, and it is an error because reaching it means the
12/// operation did not produce what was asked for. The findings say why, in the
13/// order two runs agree on.
14///
15/// It derefs to the findings, so a caller that wants the list takes it as a
16/// slice; `for finding in &set` iterates without naming [`Deref`] at all.
17///
18/// # Examples
19///
20/// ```
21/// use molframe_core::{Code, Diagnostic, Findings};
22///
23/// let findings = Findings::from(Diagnostic::new(Code::E1001));
24/// assert_eq!(findings.len(), 1);
25/// assert_eq!(findings.code(), Some(Code::E1001));
26///
27/// // The order `Diagnostics::finish` produces, and a report that reads it.
28/// println!("{findings}");
29/// ```
30///
31/// [`Deref`]: std::ops::Deref
32#[derive(Clone, Debug)]
33pub struct Findings(Vec<Diagnostic>);
34
35impl Findings {
36 /// The code of the finding that stopped the operation, if any.
37 ///
38 /// A stable identity to branch on, for a caller that would rather not match
39 /// on message text. `None` when the set is empty — a set that stopped
40 /// nothing rather than a set that failed to say why.
41 #[must_use]
42 pub fn code(&self) -> Option<Code> {
43 self.0.first().map(Diagnostic::code)
44 }
45
46 /// The findings.
47 ///
48 /// Declared rather than inherited from [`Deref`], whose `[Diagnostic]` target
49 /// reaches `[T]::as_slice` — an unstable inherent method that shadows a stable
50 /// reading of the same call. [`Diagnostics`](super::Diagnostics) names it the
51 /// same way.
52 ///
53 /// [`Deref`]: std::ops::Deref
54 #[must_use]
55 pub fn as_slice(&self) -> &[Diagnostic] {
56 &self.0
57 }
58
59 /// Consumes the set, returning the findings.
60 #[must_use]
61 pub fn into_vec(self) -> Vec<Diagnostic> {
62 self.0
63 }
64}
65
66impl std::ops::Deref for Findings {
67 type Target = [Diagnostic];
68
69 fn deref(&self) -> &Self::Target {
70 &self.0
71 }
72}
73
74impl<'a> IntoIterator for &'a Findings {
75 type Item = &'a Diagnostic;
76 type IntoIter = std::slice::Iter<'a, Diagnostic>;
77
78 fn into_iter(self) -> Self::IntoIter {
79 self.0.iter()
80 }
81}
82
83/// Prints the first finding and how many follow it.
84///
85/// A set can hold thousands, and an error message that renders all of them is
86/// one nobody reads. The first is the worst by
87/// [`Diagnostics::finish`](super::Diagnostics::finish)'s order, which is the one
88/// worth naming.
89impl fmt::Display for Findings {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 match self.0.as_slice() {
92 [] => f.write_str("no findings"),
93 [only] => write!(f, "{only}"),
94 [first, rest @ ..] => write!(f, "{first} (and {} more)", rest.len()),
95 }
96 }
97}
98
99impl std::error::Error for Findings {}
100
101impl From<Diagnostic> for Findings {
102 fn from(finding: Diagnostic) -> Self {
103 Self(vec![finding])
104 }
105}
106
107impl From<Vec<Diagnostic>> for Findings {
108 fn from(findings: Vec<Diagnostic>) -> Self {
109 Self(findings)
110 }
111}
112
113impl From<Findings> for Vec<Diagnostic> {
114 fn from(findings: Findings) -> Self {
115 findings.0
116 }
117}
118
119#[cfg(test)]
120#[path = "findings_tests.rs"]
121mod tests;