use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Kind {
Error,
Warning,
}
impl Kind {
const fn letter(self) -> char {
match self {
Self::Error => 'E',
Self::Warning => 'W',
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Severity {
Info,
Loose,
Strict,
Invalidating,
Breaking,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub enum Strictness {
Strict,
#[default]
Medium,
Loose,
}
impl Severity {
#[must_use]
pub const fn is_error(self, strictness: Strictness) -> bool {
let floor = match strictness {
Strictness::Strict => Self::Strict,
Strictness::Medium => Self::Invalidating,
Strictness::Loose => Self::Breaking,
};
(self as u8) >= (floor as u8)
}
#[must_use]
pub const fn label(self) -> &'static str {
match self {
Self::Info => "note",
Self::Loose | Self::Strict => "warning",
Self::Invalidating | Self::Breaking => "error",
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Code {
kind: Kind,
number: u16,
}
impl Code {
#[must_use]
pub const fn new(kind: Kind, number: u16) -> Self {
Self { kind, number }
}
#[must_use]
pub const fn kind(self) -> Kind {
self.kind
}
#[must_use]
pub const fn number(self) -> u16 {
self.number
}
#[must_use]
pub const fn class(self) -> Class {
Class::of(self.number)
}
fn entry(self) -> Option<&'static Entry> {
let registry = super::registry::ENTRIES;
let found = registry
.binary_search_by(|entry| entry.code.cmp(&self))
.ok()?;
registry.get(found)
}
#[must_use]
pub fn severity(self) -> Severity {
self.entry()
.map_or(Severity::Breaking, |entry| entry.severity)
}
#[must_use]
pub fn cause(self) -> &'static str {
self.entry()
.map_or("unregistered diagnostic code", |entry| entry.cause)
}
#[must_use]
pub fn remedy(self) -> &'static str {
self.entry()
.map_or("report this as a molframe bug", |entry| entry.remedy)
}
#[must_use]
pub fn is_registered(self) -> bool {
self.entry().is_some()
}
pub fn registered() -> impl Iterator<Item = Self> {
super::registry::ENTRIES.iter().map(|entry| entry.code)
}
}
impl fmt::Display for Code {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MOLFRAME-{}{:04}", self.kind.letter(), self.number)
}
}
impl fmt::Debug for Code {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[non_exhaustive]
pub enum Class {
Syntax,
Schema,
Consistency,
Conversion,
Geometry,
Policy,
Resource,
Internal,
}
impl Class {
const fn of(number: u16) -> Self {
match number / 1000 {
1 => Self::Syntax,
2 => Self::Schema,
3 => Self::Consistency,
4 => Self::Conversion,
5 => Self::Geometry,
6 => Self::Policy,
7 => Self::Resource,
_ => Self::Internal,
}
}
}
pub(super) struct Entry {
pub(super) code: Code,
pub(super) severity: Severity,
pub(super) cause: &'static str,
pub(super) remedy: &'static str,
}
#[cfg(test)]
#[path = "code_tests.rs"]
mod tests;