use alloc::vec::Vec;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[repr(u8)]
#[non_exhaustive]
pub enum ErrorKind {
Syntax,
VariantKeyMismatch,
MissingFallbackVariant,
MissingSelectorAnnotation,
DuplicateDeclaration,
DuplicateOptionName,
DuplicateVariant,
UnresolvedVariable,
UnknownFunction,
BadSelector,
BadOperand,
BadOption,
BadVariantKey,
UnsupportedOperation,
MessageFunctionError,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum ErrorClass {
Syntax,
DataModel,
Resolution,
MessageFunction,
}
impl ErrorKind {
pub const ALL: &'static [ErrorKind] = &[
ErrorKind::Syntax,
ErrorKind::VariantKeyMismatch,
ErrorKind::MissingFallbackVariant,
ErrorKind::MissingSelectorAnnotation,
ErrorKind::DuplicateDeclaration,
ErrorKind::DuplicateOptionName,
ErrorKind::DuplicateVariant,
ErrorKind::UnresolvedVariable,
ErrorKind::UnknownFunction,
ErrorKind::BadSelector,
ErrorKind::BadOperand,
ErrorKind::BadOption,
ErrorKind::BadVariantKey,
ErrorKind::UnsupportedOperation,
ErrorKind::MessageFunctionError,
];
pub const fn class(self) -> ErrorClass {
match self {
ErrorKind::Syntax => ErrorClass::Syntax,
ErrorKind::VariantKeyMismatch
| ErrorKind::MissingFallbackVariant
| ErrorKind::MissingSelectorAnnotation
| ErrorKind::DuplicateDeclaration
| ErrorKind::DuplicateOptionName
| ErrorKind::DuplicateVariant => ErrorClass::DataModel,
ErrorKind::UnresolvedVariable | ErrorKind::UnknownFunction | ErrorKind::BadSelector => {
ErrorClass::Resolution
}
ErrorKind::BadOperand
| ErrorKind::BadOption
| ErrorKind::BadVariantKey
| ErrorKind::UnsupportedOperation
| ErrorKind::MessageFunctionError => ErrorClass::MessageFunction,
}
}
#[cfg(feature = "suite-names")]
#[doc(hidden)]
pub fn suite_name(self) -> &'static str {
match self {
ErrorKind::Syntax => "syntax-error",
ErrorKind::VariantKeyMismatch => "variant-key-mismatch",
ErrorKind::MissingFallbackVariant => "missing-fallback-variant",
ErrorKind::MissingSelectorAnnotation => "missing-selector-annotation",
ErrorKind::DuplicateDeclaration => "duplicate-declaration",
ErrorKind::DuplicateOptionName => "duplicate-option-name",
ErrorKind::DuplicateVariant => "duplicate-variant",
ErrorKind::UnresolvedVariable => "unresolved-variable",
ErrorKind::UnknownFunction => "unknown-function",
ErrorKind::BadSelector => "bad-selector",
ErrorKind::BadOperand => "bad-operand",
ErrorKind::BadOption => "bad-option",
ErrorKind::BadVariantKey => "bad-variant-key",
ErrorKind::UnsupportedOperation => "unsupported-operation",
ErrorKind::MessageFunctionError => "message-function-error",
}
}
#[cfg(feature = "suite-names")]
#[doc(hidden)]
pub fn from_suite_name(s: &str) -> Option<ErrorKind> {
ErrorKind::ALL.iter().copied().find(|k| k.suite_name() == s)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Span {
pub start: u32,
pub end: u32,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct Diagnostic {
pub kind: ErrorKind,
pub code: u16,
pub span: Option<Span>,
}
impl Diagnostic {
pub const fn new(kind: ErrorKind, code: u16, span: Option<Span>) -> Diagnostic {
Diagnostic { kind, code, span }
}
}
#[derive(Clone, Default, PartialEq, Eq, Hash, Debug)]
pub struct Diagnostics(Vec<Diagnostic>);
impl Diagnostics {
pub const fn new() -> Self {
Diagnostics(Vec::new())
}
pub fn push(&mut self, d: Diagnostic) {
self.0.push(d);
}
pub fn iter(&self) -> core::slice::Iter<'_, Diagnostic> {
self.0.iter()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn has(&self, kind: ErrorKind) -> bool {
self.0.iter().any(|d| d.kind == kind)
}
pub fn has_class(&self, class: ErrorClass) -> bool {
self.0.iter().any(|d| d.kind.class() == class)
}
pub fn into_vec(self) -> Vec<Diagnostic> {
self.0
}
}
impl<'a> IntoIterator for &'a Diagnostics {
type Item = &'a Diagnostic;
type IntoIter = core::slice::Iter<'a, Diagnostic>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl From<Vec<Diagnostic>> for Diagnostics {
fn from(v: Vec<Diagnostic>) -> Self {
Diagnostics(v)
}
}
impl Extend<Diagnostic> for Diagnostics {
fn extend<I: IntoIterator<Item = Diagnostic>>(&mut self, iter: I) {
self.0.extend(iter);
}
}
#[cfg(test)]
mod tests {
use super::{Diagnostic, Diagnostics, ErrorClass, ErrorKind, Span};
#[test]
fn classes() {
use ErrorClass::{DataModel, MessageFunction, Resolution, Syntax};
let classes: alloc::vec::Vec<ErrorClass> =
ErrorKind::ALL.iter().map(|k| k.class()).collect();
assert_eq!(
classes,
[
Syntax,
DataModel,
DataModel,
DataModel,
DataModel,
DataModel,
DataModel,
Resolution,
Resolution,
Resolution,
MessageFunction,
MessageFunction,
MessageFunction,
MessageFunction,
MessageFunction,
]
);
}
#[test]
fn discriminants_follow_declaration_order() {
for (i, k) in ErrorKind::ALL.iter().enumerate() {
assert_eq!(*k as u8 as usize, i);
}
}
#[cfg(feature = "suite-names")]
#[test]
fn suite_names_round_trip() {
for &k in ErrorKind::ALL {
assert_eq!(ErrorKind::from_suite_name(k.suite_name()), Some(k));
}
assert_eq!(
ErrorKind::from_suite_name("bad-option"),
Some(ErrorKind::BadOption)
);
assert_eq!(ErrorKind::from_suite_name("Bad-Option"), None);
assert_eq!(ErrorKind::from_suite_name(""), None);
}
#[test]
fn diagnostics_queries() {
let mut d = Diagnostics::new();
assert!(d.is_empty());
assert_eq!(d.len(), 0);
assert!(!d.has(ErrorKind::Syntax));
d.push(Diagnostic {
kind: ErrorKind::DuplicateVariant,
code: 0,
span: Some(Span { start: 1, end: 4 }),
});
assert_eq!(d.len(), 1);
assert!(d.has(ErrorKind::DuplicateVariant));
assert!(!d.has(ErrorKind::Syntax));
assert!(d.has_class(ErrorClass::DataModel));
assert!(!d.has_class(ErrorClass::Syntax));
assert_eq!(d.iter().count(), 1);
let v = d.into_vec();
assert_eq!(v[0].span, Some(Span { start: 1, end: 4 }));
}
}