use std::fmt::{self, Write};
use super::super::lexer::{LexerError, LexerLimitHit, Position, Token};
#[derive(Debug, Clone)]
pub struct ParseError {
pub message: String,
pub position: Position,
pub expected: Vec<String>,
pub kind: ParseErrorKind,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseErrorKind {
Syntax,
DepthLimit {
limit_name: &'static str,
value: usize,
},
InputTooLarge {
limit_name: &'static str,
value: usize,
},
IdentifierTooLong {
limit_name: &'static str,
value: usize,
},
ValueOutOfRange {
field: &'static str,
constraint: &'static str,
},
}
impl ParseError {
pub fn new(message: impl Into<String>, position: Position) -> Self {
Self {
message: message.into(),
position,
expected: Vec::new(),
kind: ParseErrorKind::Syntax,
}
}
pub fn expected(expected: Vec<&str>, found: &Token, position: Position) -> Self {
Self {
message: format!("Unexpected token: {}", SafeTokenDisplay(found)),
position,
expected: expected.into_iter().map(|s| s.to_string()).collect(),
kind: ParseErrorKind::Syntax,
}
}
pub fn depth_limit(limit_name: &'static str, value: usize, position: Position) -> Self {
Self {
message: format!(
"recursion depth limit exceeded ({} = {})",
limit_name, value
),
position,
expected: Vec::new(),
kind: ParseErrorKind::DepthLimit { limit_name, value },
}
}
pub fn input_too_large(limit_name: &'static str, value: usize, position: Position) -> Self {
Self {
message: format!(
"input exceeds maximum size ({} = {} bytes)",
limit_name, value
),
position,
expected: Vec::new(),
kind: ParseErrorKind::InputTooLarge { limit_name, value },
}
}
pub fn identifier_too_long(limit_name: &'static str, value: usize, position: Position) -> Self {
Self {
message: format!(
"identifier exceeds maximum length ({} = {} chars)",
limit_name, value
),
position,
expected: Vec::new(),
kind: ParseErrorKind::IdentifierTooLong { limit_name, value },
}
}
pub fn value_out_of_range(
field: &'static str,
constraint: &'static str,
position: Position,
) -> Self {
Self {
message: format!("{} {}", field, constraint),
position,
expected: Vec::new(),
kind: ParseErrorKind::ValueOutOfRange { field, constraint },
}
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Parse error at {}: {}", self.position, self.message)?;
if !self.expected.is_empty() {
write!(f, " (expected: {})", self.expected.join(", "))?;
}
Ok(())
}
}
impl std::error::Error for ParseError {}
pub struct SafeTokenDisplay<'a>(pub &'a Token);
impl fmt::Display for SafeTokenDisplay<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Token::Ident(s) => write_escaped(f, s),
Token::String(s) => {
f.write_str("'")?;
write_escaped(f, s)?;
f.write_str("'")
}
Token::JsonLiteral(s) => write_escaped(f, s),
Token::Integer(_) | Token::Float(_) => fmt::Display::fmt(self.0, f),
other => fmt::Display::fmt(other, f),
}
}
}
fn write_escaped(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result {
for ch in s.chars() {
for esc in ch.escape_debug() {
f.write_char(esc)?;
}
}
Ok(())
}
impl From<LexerError> for ParseError {
fn from(e: LexerError) -> Self {
let kind = match &e.limit_hit {
Some(LexerLimitHit::IdentifierTooLong { limit_name, value }) => {
ParseErrorKind::IdentifierTooLong {
limit_name,
value: *value,
}
}
None => ParseErrorKind::Syntax,
};
ParseError {
message: e.message,
position: e.position,
expected: Vec::new(),
kind,
}
}
}