use crate::span::Span;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum SchemaError {
Lexer(String, Span),
UnterminatedString(Span),
InvalidNumber(String, Span),
UnexpectedCharacter(char, Span),
Parse(String, Span),
Validation(String, Span),
Warning(String, Span),
Other(String),
}
impl SchemaError {
pub fn format_with_source(&self, source: &str) -> String {
match self {
SchemaError::Lexer(msg, span)
| SchemaError::Parse(msg, span)
| SchemaError::Validation(msg, span)
| SchemaError::Warning(msg, span) => {
let (start_pos, _) = span.to_positions(source);
format!("{} at {}", msg, start_pos)
}
SchemaError::UnterminatedString(span) => {
let (start_pos, _) = span.to_positions(source);
format!("Unterminated string literal at {}", start_pos)
}
SchemaError::InvalidNumber(num, span) => {
let (start_pos, _) = span.to_positions(source);
format!("Invalid number '{}' at {}", num, start_pos)
}
SchemaError::UnexpectedCharacter(ch, span) => {
let (start_pos, _) = span.to_positions(source);
format!("Unexpected character '{}' at {}", ch, start_pos)
}
SchemaError::Other(msg) => msg.clone(),
}
}
pub fn format_with_file(&self, filepath: &str, source: &str) -> String {
match self {
SchemaError::Lexer(msg, span)
| SchemaError::Parse(msg, span)
| SchemaError::Validation(msg, span)
| SchemaError::Warning(msg, span) => {
let (start_pos, _) = span.to_positions(source);
format!(
"{}:{}:{}: {}",
filepath, start_pos.line, start_pos.column, msg
)
}
SchemaError::UnterminatedString(span) => {
let (start_pos, _) = span.to_positions(source);
format!(
"{}:{}:{}: Unterminated string literal",
filepath, start_pos.line, start_pos.column
)
}
SchemaError::InvalidNumber(num, span) => {
let (start_pos, _) = span.to_positions(source);
format!(
"{}:{}:{}: Invalid number '{}'",
filepath, start_pos.line, start_pos.column, num
)
}
SchemaError::UnexpectedCharacter(ch, span) => {
let (start_pos, _) = span.to_positions(source);
format!(
"{}:{}:{}: Unexpected character '{}'",
filepath, start_pos.line, start_pos.column, ch
)
}
SchemaError::Other(msg) => msg.clone(),
}
}
pub fn span(&self) -> Option<Span> {
match self {
SchemaError::Lexer(_, span)
| SchemaError::Parse(_, span)
| SchemaError::Validation(_, span)
| SchemaError::Warning(_, span)
| SchemaError::UnterminatedString(span)
| SchemaError::InvalidNumber(_, span)
| SchemaError::UnexpectedCharacter(_, span) => Some(*span),
SchemaError::Other(_) => None,
}
}
}
impl fmt::Display for SchemaError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SchemaError::Lexer(msg, _) => write!(f, "Lexer error: {}", msg),
SchemaError::UnterminatedString(_) => write!(f, "Unterminated string literal"),
SchemaError::InvalidNumber(num, _) => write!(f, "Invalid number: {}", num),
SchemaError::UnexpectedCharacter(ch, _) => write!(f, "Unexpected character: '{}'", ch),
SchemaError::Parse(msg, _) => write!(f, "Parse error: {}", msg),
SchemaError::Validation(msg, _) => write!(f, "Validation error: {}", msg),
SchemaError::Warning(msg, _) => write!(f, "Warning: {}", msg),
SchemaError::Other(msg) => write!(f, "{}", msg),
}
}
}
impl std::error::Error for SchemaError {}
pub type Result<T> = std::result::Result<T, SchemaError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let span = Span::new(0, 5);
let err = SchemaError::Lexer("test error".to_string(), span);
assert_eq!(err.to_string(), "Lexer error: test error");
}
#[test]
fn test_error_span() {
let span = Span::new(10, 15);
let err = SchemaError::UnexpectedCharacter('#', span);
assert_eq!(err.span(), Some(span));
let err_no_span = SchemaError::Other("generic".to_string());
assert_eq!(err_no_span.span(), None);
}
#[test]
fn test_format_with_source() {
let source = "hello\nworld";
let span = Span::new(6, 11); let err = SchemaError::Lexer("unexpected token".to_string(), span);
let formatted = err.format_with_source(source);
assert!(formatted.contains("2:1")); }
#[test]
fn test_format_with_file() {
let source = "hello\nworld";
let span = Span::new(6, 11); let err = SchemaError::Lexer("unexpected token".to_string(), span);
let formatted = err.format_with_file("schema.nautilus", source);
assert_eq!(formatted, "schema.nautilus:2:1: unexpected token");
}
}