1use alloc::vec::Vec;
4
5#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
9#[repr(u8)]
10#[non_exhaustive]
11pub enum ErrorKind {
12 Syntax,
14 VariantKeyMismatch,
16 MissingFallbackVariant,
18 MissingSelectorAnnotation,
20 DuplicateDeclaration,
22 DuplicateOptionName,
24 DuplicateVariant,
26 UnresolvedVariable,
28 UnknownFunction,
30 BadSelector,
32 BadOperand,
34 BadOption,
36 BadVariantKey,
38 UnsupportedOperation,
42 MessageFunctionError,
45}
46
47#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
49#[non_exhaustive]
50pub enum ErrorClass {
51 Syntax,
53 DataModel,
55 Resolution,
57 MessageFunction,
59}
60
61impl ErrorKind {
62 pub const ALL: &'static [ErrorKind] = &[
64 ErrorKind::Syntax,
65 ErrorKind::VariantKeyMismatch,
66 ErrorKind::MissingFallbackVariant,
67 ErrorKind::MissingSelectorAnnotation,
68 ErrorKind::DuplicateDeclaration,
69 ErrorKind::DuplicateOptionName,
70 ErrorKind::DuplicateVariant,
71 ErrorKind::UnresolvedVariable,
72 ErrorKind::UnknownFunction,
73 ErrorKind::BadSelector,
74 ErrorKind::BadOperand,
75 ErrorKind::BadOption,
76 ErrorKind::BadVariantKey,
77 ErrorKind::UnsupportedOperation,
78 ErrorKind::MessageFunctionError,
79 ];
80
81 pub const fn class(self) -> ErrorClass {
83 match self {
84 ErrorKind::Syntax => ErrorClass::Syntax,
85 ErrorKind::VariantKeyMismatch
86 | ErrorKind::MissingFallbackVariant
87 | ErrorKind::MissingSelectorAnnotation
88 | ErrorKind::DuplicateDeclaration
89 | ErrorKind::DuplicateOptionName
90 | ErrorKind::DuplicateVariant => ErrorClass::DataModel,
91 ErrorKind::UnresolvedVariable | ErrorKind::UnknownFunction | ErrorKind::BadSelector => {
92 ErrorClass::Resolution
93 }
94 ErrorKind::BadOperand
95 | ErrorKind::BadOption
96 | ErrorKind::BadVariantKey
97 | ErrorKind::UnsupportedOperation
98 | ErrorKind::MessageFunctionError => ErrorClass::MessageFunction,
99 }
100 }
101
102 #[cfg(feature = "suite-names")]
107 #[doc(hidden)]
108 pub fn suite_name(self) -> &'static str {
109 match self {
110 ErrorKind::Syntax => "syntax-error",
111 ErrorKind::VariantKeyMismatch => "variant-key-mismatch",
112 ErrorKind::MissingFallbackVariant => "missing-fallback-variant",
113 ErrorKind::MissingSelectorAnnotation => "missing-selector-annotation",
114 ErrorKind::DuplicateDeclaration => "duplicate-declaration",
115 ErrorKind::DuplicateOptionName => "duplicate-option-name",
116 ErrorKind::DuplicateVariant => "duplicate-variant",
117 ErrorKind::UnresolvedVariable => "unresolved-variable",
118 ErrorKind::UnknownFunction => "unknown-function",
119 ErrorKind::BadSelector => "bad-selector",
120 ErrorKind::BadOperand => "bad-operand",
121 ErrorKind::BadOption => "bad-option",
122 ErrorKind::BadVariantKey => "bad-variant-key",
123 ErrorKind::UnsupportedOperation => "unsupported-operation",
124 ErrorKind::MessageFunctionError => "message-function-error",
125 }
126 }
127
128 #[cfg(feature = "suite-names")]
130 #[doc(hidden)]
131 pub fn from_suite_name(s: &str) -> Option<ErrorKind> {
132 ErrorKind::ALL.iter().copied().find(|k| k.suite_name() == s)
133 }
134}
135
136#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
138pub struct Span {
139 pub start: u32,
141 pub end: u32,
143}
144
145#[derive(Clone, PartialEq, Eq, Hash, Debug)]
148#[non_exhaustive]
149pub struct Diagnostic {
150 pub kind: ErrorKind,
152 pub code: u16,
155 pub span: Option<Span>,
157}
158
159impl Diagnostic {
160 pub const fn new(kind: ErrorKind, code: u16, span: Option<Span>) -> Diagnostic {
163 Diagnostic { kind, code, span }
164 }
165}
166
167#[derive(Clone, Default, PartialEq, Eq, Hash, Debug)]
169pub struct Diagnostics(Vec<Diagnostic>);
170
171impl Diagnostics {
172 pub const fn new() -> Self {
174 Diagnostics(Vec::new())
175 }
176
177 pub fn push(&mut self, d: Diagnostic) {
179 self.0.push(d);
180 }
181
182 pub fn iter(&self) -> core::slice::Iter<'_, Diagnostic> {
184 self.0.iter()
185 }
186
187 pub fn len(&self) -> usize {
189 self.0.len()
190 }
191
192 pub fn is_empty(&self) -> bool {
194 self.0.is_empty()
195 }
196
197 pub fn has(&self, kind: ErrorKind) -> bool {
199 self.0.iter().any(|d| d.kind == kind)
200 }
201
202 pub fn has_class(&self, class: ErrorClass) -> bool {
204 self.0.iter().any(|d| d.kind.class() == class)
205 }
206
207 pub fn into_vec(self) -> Vec<Diagnostic> {
209 self.0
210 }
211}
212
213impl<'a> IntoIterator for &'a Diagnostics {
214 type Item = &'a Diagnostic;
215 type IntoIter = core::slice::Iter<'a, Diagnostic>;
216
217 fn into_iter(self) -> Self::IntoIter {
218 self.iter()
219 }
220}
221
222impl From<Vec<Diagnostic>> for Diagnostics {
223 fn from(v: Vec<Diagnostic>) -> Self {
224 Diagnostics(v)
225 }
226}
227
228impl Extend<Diagnostic> for Diagnostics {
229 fn extend<I: IntoIterator<Item = Diagnostic>>(&mut self, iter: I) {
230 self.0.extend(iter);
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::{Diagnostic, Diagnostics, ErrorClass, ErrorKind, Span};
237
238 #[test]
239 fn classes() {
240 use ErrorClass::{DataModel, MessageFunction, Resolution, Syntax};
241 let classes: alloc::vec::Vec<ErrorClass> =
242 ErrorKind::ALL.iter().map(|k| k.class()).collect();
243 assert_eq!(
244 classes,
245 [
246 Syntax,
247 DataModel,
248 DataModel,
249 DataModel,
250 DataModel,
251 DataModel,
252 DataModel,
253 Resolution,
254 Resolution,
255 Resolution,
256 MessageFunction,
257 MessageFunction,
258 MessageFunction,
259 MessageFunction,
260 MessageFunction,
261 ]
262 );
263 }
264
265 #[test]
266 fn discriminants_follow_declaration_order() {
267 for (i, k) in ErrorKind::ALL.iter().enumerate() {
268 assert_eq!(*k as u8 as usize, i);
269 }
270 }
271
272 #[cfg(feature = "suite-names")]
273 #[test]
274 fn suite_names_round_trip() {
275 for &k in ErrorKind::ALL {
276 assert_eq!(ErrorKind::from_suite_name(k.suite_name()), Some(k));
277 }
278 assert_eq!(
279 ErrorKind::from_suite_name("bad-option"),
280 Some(ErrorKind::BadOption)
281 );
282 assert_eq!(ErrorKind::from_suite_name("Bad-Option"), None);
283 assert_eq!(ErrorKind::from_suite_name(""), None);
284 }
285
286 #[test]
287 fn diagnostics_queries() {
288 let mut d = Diagnostics::new();
289 assert!(d.is_empty());
290 assert_eq!(d.len(), 0);
291 assert!(!d.has(ErrorKind::Syntax));
292 d.push(Diagnostic {
293 kind: ErrorKind::DuplicateVariant,
294 code: 0,
295 span: Some(Span { start: 1, end: 4 }),
296 });
297 assert_eq!(d.len(), 1);
298 assert!(d.has(ErrorKind::DuplicateVariant));
299 assert!(!d.has(ErrorKind::Syntax));
300 assert!(d.has_class(ErrorClass::DataModel));
301 assert!(!d.has_class(ErrorClass::Syntax));
302 assert_eq!(d.iter().count(), 1);
303 let v = d.into_vec();
304 assert_eq!(v[0].span, Some(Span { start: 1, end: 4 }));
305 }
306}