use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ErrorSeverity {
Warning,
Error,
Fatal,
}
impl fmt::Display for ErrorSeverity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Warning => write!(f, "warning"),
Self::Error => write!(f, "error"),
Self::Fatal => write!(f, "fatal error"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct SourceLocation {
pub line: u32,
pub column: u32,
pub byte_offset: usize,
}
impl fmt::Display for SourceLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.line, self.column)
}
}
#[derive(Debug, Clone)]
pub struct ParseDiagnostic {
pub severity: ErrorSeverity,
pub message: String,
pub location: SourceLocation,
}
impl fmt::Display for ParseDiagnostic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}: {} at {}",
self.severity, self.message, self.location
)
}
}
#[derive(Debug, Clone)]
pub struct ParseError {
pub message: String,
pub location: SourceLocation,
pub diagnostics: Vec<ParseDiagnostic>,
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "parse error at {}: {}", self.location, self.message)
}
}
impl std::error::Error for ParseError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_source_location_display() {
let loc = SourceLocation {
line: 10,
column: 5,
byte_offset: 42,
};
assert_eq!(loc.to_string(), "10:5");
}
#[test]
fn test_parse_error_display() {
let err = ParseError {
message: "unexpected end of input".to_string(),
location: SourceLocation {
line: 1,
column: 15,
byte_offset: 14,
},
diagnostics: vec![],
};
assert_eq!(
err.to_string(),
"parse error at 1:15: unexpected end of input"
);
}
#[test]
fn test_parse_diagnostic_display() {
let diag = ParseDiagnostic {
severity: ErrorSeverity::Warning,
message: "attribute value not quoted".to_string(),
location: SourceLocation {
line: 3,
column: 10,
byte_offset: 50,
},
};
assert_eq!(
diag.to_string(),
"warning: attribute value not quoted at 3:10"
);
}
#[test]
fn test_error_severity_display() {
assert_eq!(ErrorSeverity::Warning.to_string(), "warning");
assert_eq!(ErrorSeverity::Error.to_string(), "error");
assert_eq!(ErrorSeverity::Fatal.to_string(), "fatal error");
}
#[test]
fn test_parse_error_is_error_trait() {
let err = ParseError {
message: "test".to_string(),
location: SourceLocation::default(),
diagnostics: vec![],
};
let _: &dyn std::error::Error = &err;
}
}