1#![warn(missing_docs)]
4
5#[cfg(test)]
6pub(crate) mod tests;
7
8use std::fmt::{Debug, Display};
9
10use serde::{Deserialize, Serialize};
11
12pub trait Diagnostic: Debug + Display {
14 fn help(&self) -> Option<Box<dyn Display>> {
17 None
18 }
19
20 fn severity(&self) -> Severity {
24 Default::default()
25 }
26}
27
28#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)]
30pub enum Severity {
31 Info,
33 #[default]
35 Warning,
36 Error,
38}
39
40#[derive(Debug, Clone)]
42pub struct IssueSet<T> {
43 issues: Vec<T>,
44 num_errors: usize,
45 num_warnings: usize,
46}
47
48impl<T> IssueSet<T> {
49 #[inline]
51 pub fn new() -> Self {
52 Self {
53 issues: Vec::new(),
54 num_errors: 0,
55 num_warnings: 0,
56 }
57 }
58
59 #[inline]
61 pub fn iter(&self) -> impl Iterator<Item = &T> {
62 self.issues.iter()
63 }
64
65 #[inline]
67 pub fn len(&self) -> usize {
68 self.issues.len()
69 }
70
71 #[inline]
73 pub fn is_empty(&self) -> bool {
74 self.issues.is_empty()
75 }
76}
77
78impl<T: Diagnostic> IssueSet<T> {
79 #[inline]
81 pub fn add(&mut self, issue: T) {
82 let severity = issue.severity();
83 match severity {
84 Severity::Error => self.num_errors += 1,
85 Severity::Warning => self.num_warnings += 1,
86 _ => (),
87 };
88 self.issues.push(issue);
89 }
90
91 pub fn has_error(&self) -> bool {
96 self.num_errors > 0
97 }
98
99 #[inline]
101 pub fn num_errors(&self) -> usize {
102 self.num_errors
103 }
104
105 pub fn has_warning(&self) -> bool {
110 self.num_warnings > 0
111 }
112
113 #[inline]
115 pub fn num_warnings(&self) -> usize {
116 self.num_warnings
117 }
118}
119
120impl<T> IntoIterator for IssueSet<T> {
121 type Item = T;
122 type IntoIter = <std::vec::Vec<T> as IntoIterator>::IntoIter;
123 fn into_iter(self) -> Self::IntoIter {
124 self.issues.into_iter()
125 }
126}
127
128impl<T> Default for IssueSet<T> {
129 fn default() -> Self {
130 Self::new()
131 }
132}
133
134impl Severity {
135 #[inline]
137 pub const fn as_tracing_level(&self) -> tracing::Level {
138 match *self {
139 Self::Info => tracing::Level::INFO,
140 Self::Warning => tracing::Level::WARN,
141 Self::Error => tracing::Level::ERROR,
142 }
143 }
144
145 #[inline]
147 pub fn is_error(&self) -> bool {
148 matches!(*self, Self::Error)
149 }
150}
151
152impl Display for Severity {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 match *self {
155 Self::Info => write!(f, "info"),
156 Self::Warning => write!(f, "warning"),
157 Self::Error => write!(f, "error"),
158 }
159 }
160}
161
162impl<T: Display> Display for IssueSet<T> {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 for issue in self.issues.iter() {
165 writeln!(f, "{}", issue)?;
166 }
167 Ok(())
168 }
169}