vrl/diagnostic/
diagnostic.rs1use std::ops::{Deref, DerefMut};
2
3use codespan_reporting::diagnostic;
4
5use super::{DiagnosticMessage, Label, Note, Severity, Span};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct Diagnostic {
9 pub severity: Severity,
10 pub code: usize,
11 pub message: String,
12 pub labels: Vec<Label>,
13 pub notes: Vec<Note>,
14}
15
16impl Diagnostic {
17 pub fn error(code: usize, message: impl ToString) -> Self {
18 Self::new(Severity::Error, code, message, vec![], vec![])
19 }
20
21 pub fn bug(code: usize, message: impl ToString) -> Self {
22 Self::new(Severity::Bug, code, message, vec![], vec![])
23 }
24
25 pub fn new(
26 severity: Severity,
27 code: usize,
28 message: impl ToString,
29 labels: Vec<Label>,
30 notes: Vec<Note>,
31 ) -> Self {
32 Self {
33 severity,
34 code,
35 message: message.to_string(),
36 labels,
37 notes,
38 }
39 }
40
41 #[must_use]
42 pub fn with_primary(self, message: impl ToString, span: impl Into<Span>) -> Self {
43 self.with_label(Label::primary(message, span.into()))
44 }
45
46 #[must_use]
47 pub fn with_context(self, message: impl ToString, span: impl Into<Span>) -> Self {
48 self.with_label(Label::context(message, span.into()))
49 }
50
51 #[must_use]
52 pub fn with_label(mut self, label: Label) -> Self {
53 self.labels.push(label);
54 self
55 }
56
57 #[must_use]
58 pub fn with_note(mut self, note: Note) -> Self {
59 self.notes.push(note);
60 self
61 }
62
63 #[must_use]
64 pub fn severity(&self) -> Severity {
65 self.severity
66 }
67
68 #[must_use]
69 pub fn message(&self) -> &str {
70 &self.message
71 }
72
73 #[must_use]
74 pub fn notes(&self) -> &[Note] {
75 &self.notes
76 }
77
78 #[must_use]
79 pub fn labels(&self) -> &[Label] {
80 &self.labels
81 }
82
83 #[inline]
86 #[must_use]
87 pub fn is_problem(&self) -> bool {
88 self.severity.is_error() || self.severity.is_bug()
89 }
90
91 #[inline]
93 #[must_use]
94 pub fn is_bug(&self) -> bool {
95 self.severity.is_bug()
96 }
97
98 #[inline]
100 #[must_use]
101 pub fn is_error(&self) -> bool {
102 self.severity.is_error()
103 }
104
105 #[inline]
108 #[must_use]
109 pub fn is_warning(&self) -> bool {
110 self.severity.is_warning()
111 }
112
113 #[inline]
115 #[must_use]
116 pub fn is_note(&self) -> bool {
117 self.severity.is_note()
118 }
119}
120
121impl From<Box<dyn DiagnosticMessage>> for Diagnostic {
122 fn from(message: Box<dyn DiagnosticMessage>) -> Self {
123 Self {
124 severity: message.severity(),
125 code: message.code(),
126 message: message.message(),
127 labels: message.labels(),
128 notes: message.notes(),
129 }
130 }
131}
132
133impl From<Diagnostic> for diagnostic::Diagnostic<()> {
134 fn from(diag: Diagnostic) -> Self {
135 let mut notes = diag.notes.clone();
136
137 if diag.code >= 100 && diag.code <= 110 {
139 notes.push(Note::SeeCodeDocs(diag.code));
140 }
141
142 notes.push(Note::SeeLangDocs);
143 notes.push(Note::SeeRepl);
144
145 diagnostic::Diagnostic {
146 severity: diag.severity.into(),
147 code: Some(format!("E{:03}", diag.code)),
148 message: diag.message.clone(),
149 labels: diag.labels.iter().cloned().map(Into::into).collect(),
150 notes: notes.iter().map(ToString::to_string).collect(),
151 }
152 }
153}
154
155#[derive(Debug, Clone, Default, PartialEq, Eq)]
158pub struct DiagnosticList(Vec<Diagnostic>);
159
160impl DiagnosticList {
161 pub fn into_result(self) -> std::result::Result<DiagnosticList, DiagnosticList> {
165 if self.is_err() {
166 return Err(self);
167 }
168
169 Ok(self)
170 }
171
172 #[must_use]
174 pub fn is_err(&self) -> bool {
175 self.0.iter().any(Diagnostic::is_problem)
176 }
177
178 #[must_use]
180 pub fn bugs(&self) -> Vec<&Diagnostic> {
181 self.0.iter().filter(|d| d.is_bug()).collect()
182 }
183
184 #[must_use]
186 pub fn errors(&self) -> Vec<&Diagnostic> {
187 self.0.iter().filter(|d| d.is_error()).collect()
188 }
189
190 #[must_use]
192 pub fn warnings(&self) -> Vec<&Diagnostic> {
193 self.0.iter().filter(|d| d.is_warning()).collect()
194 }
195
196 #[must_use]
198 pub fn notes(&self) -> Vec<&Diagnostic> {
199 self.0.iter().filter(|d| d.is_note()).collect()
200 }
201
202 #[must_use]
204 pub fn has_bugs(&self) -> bool {
205 self.0.iter().any(Diagnostic::is_bug)
206 }
207
208 #[must_use]
210 pub fn has_errors(&self) -> bool {
211 self.0.iter().any(Diagnostic::is_error)
212 }
213
214 #[must_use]
216 pub fn has_warnings(&self) -> bool {
217 self.0.iter().any(Diagnostic::is_warning)
218 }
219
220 #[must_use]
222 pub fn has_notes(&self) -> bool {
223 self.0.iter().any(Diagnostic::is_note)
224 }
225}
226
227impl Deref for DiagnosticList {
228 type Target = Vec<Diagnostic>;
229
230 fn deref(&self) -> &Self::Target {
231 &self.0
232 }
233}
234
235impl DerefMut for DiagnosticList {
236 fn deref_mut(&mut self) -> &mut Self::Target {
237 &mut self.0
238 }
239}
240
241impl IntoIterator for DiagnosticList {
242 type Item = Diagnostic;
243 type IntoIter = std::vec::IntoIter<Diagnostic>;
244
245 fn into_iter(self) -> Self::IntoIter {
246 self.0.into_iter()
247 }
248}
249
250impl<T: Into<Diagnostic>> From<Vec<T>> for DiagnosticList {
251 fn from(diagnostics: Vec<T>) -> Self {
252 Self(diagnostics.into_iter().map(Into::into).collect())
253 }
254}
255
256impl<T: Into<Diagnostic>> From<T> for DiagnosticList {
257 fn from(diagnostic: T) -> Self {
258 Self(vec![diagnostic.into()])
259 }
260}