use crate::ast::Location;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum ErrorLevel {
Debug,
Info,
Warning,
Error,
Critical,
}
impl ErrorLevel {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
ErrorLevel::Debug => "debug",
ErrorLevel::Info => "info",
ErrorLevel::Warning => "warning",
ErrorLevel::Error => "error",
ErrorLevel::Critical => "critical",
}
}
}
impl std::fmt::Display for ErrorLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ValidationError<'a> {
pub id: &'static str,
pub level: ErrorLevel,
pub message: String,
pub location: Option<Location<'a>>,
}
impl<'a> ValidationError<'a> {
#[must_use]
pub fn new(id: &'static str, level: ErrorLevel, message: impl Into<String>) -> Self {
ValidationError {
id,
level,
message: message.into(),
location: None,
}
}
#[must_use]
pub fn at(mut self, location: Location<'a>) -> Self {
self.location = Some(location);
self
}
#[must_use]
pub fn parse_error(message: impl Into<String>) -> Self {
Self::new("parse-error", ErrorLevel::Critical, message)
}
#[must_use]
pub fn missing_opening(name: &str) -> Self {
Self::new(
"missing-opening",
ErrorLevel::Critical,
format!("Node '{name}' is missing opening"),
)
}
#[must_use]
pub fn missing_closing(name: &str) -> Self {
Self::new(
"missing-closing",
ErrorLevel::Critical,
format!("Node '{name}' is missing closing"),
)
}
#[must_use]
pub fn duplicate_attribute(name: &str) -> Self {
Self::new(
"duplicate-attribute",
ErrorLevel::Warning,
format!("Attribute '{name}' already set"),
)
}
#[must_use]
pub fn no_inline_annotations(parent: &str) -> Self {
Self::new(
"no-inline-annotations",
ErrorLevel::Error,
format!("Can't apply inline annotations to '{parent}'"),
)
}
#[must_use]
pub fn href_format_invalid() -> Self {
Self::new(
"href-format-invalid",
ErrorLevel::Error,
"The 'href' format cannot contain Markdoc tag or variable. \
URLs must be static strings.",
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ids_match_upstream() {
assert_eq!(ValidationError::parse_error("x").id, "parse-error");
assert_eq!(
ValidationError::missing_opening("foo").id,
"missing-opening"
);
assert_eq!(
ValidationError::missing_closing("foo").id,
"missing-closing"
);
assert_eq!(
ValidationError::duplicate_attribute("bar").id,
"duplicate-attribute"
);
assert_eq!(
ValidationError::no_inline_annotations("fence").id,
"no-inline-annotations"
);
assert_eq!(
ValidationError::href_format_invalid().id,
"href-format-invalid"
);
}
#[test]
fn messages_match_upstream() {
assert_eq!(
ValidationError::missing_closing("foo").message,
"Node 'foo' is missing closing"
);
assert_eq!(
ValidationError::duplicate_attribute("bar").message,
"Attribute 'bar' already set"
);
assert_eq!(
ValidationError::no_inline_annotations("fence").message,
"Can't apply inline annotations to 'fence'"
);
assert_eq!(
ValidationError::href_format_invalid().message,
"The 'href' format cannot contain Markdoc tag or variable. \
URLs must be static strings."
);
}
#[test]
fn levels_print_as_upstream_spells_them() {
assert_eq!(ErrorLevel::Warning.to_string(), "warning");
assert_eq!(ErrorLevel::Critical.to_string(), "critical");
assert!(ErrorLevel::Critical > ErrorLevel::Error);
}
}