use std::num::ParseIntError;
use std::ops::Range;
use codespan_reporting::diagnostic::{Diagnostic, Label};
use nom::error::{ErrorKind as NomErrorKind, ParseError};
use super::types::Input;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Error {
span: Range<usize>,
kind: ErrorKind,
}
impl Error {
#[must_use]
pub(crate) fn new(span: Range<usize>, kind: ErrorKind) -> Self {
Self { span, kind }
}
#[must_use]
pub fn to_diagnostic(&self) -> Diagnostic<()> {
match &self.kind {
ErrorKind::Base64AlphabetInvalidLength { length } => Diagnostic::error()
.with_message("base64 modifier alphabet must contain exactly 64 characters")
.with_labels(vec![Label::primary((), self.span.clone())
.with_message(format!("this contains {length} characters"))]),
ErrorKind::Base64AlphabetIncompatible => Diagnostic::error()
.with_message("alphabets used for base64 and base64wide must be identical")
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::CannotNegateMaskAll => Diagnostic::error()
.with_message("negating an unknown byte is not allowed")
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::ExprTooDeep => Diagnostic::error()
.with_message("too many imbricated expressions")
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::RegexClassRangeInvalid => Diagnostic::error()
.with_message("invalid regex class range, start must be <= to end")
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::RegexNonAsciiByte => Diagnostic::error()
.with_message("regex should only contain ascii bytes")
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::RegexRangeInvalid => Diagnostic::error()
.with_message("invalid regex range, start must be <= to end")
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::RegexTooDeep => Diagnostic::error()
.with_message("too many imbricated groups in the regex")
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::HexStringTooDeep => Diagnostic::error()
.with_message("too many imbricated groups in the hex string")
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::JumpAtBound => Diagnostic::error()
.with_message("a list of tokens cannot start or end with a jump")
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::JumpEmpty => Diagnostic::error()
.with_message("jump cannot have a length of 0")
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::JumpRangeInvalid { from, to } => Diagnostic::error()
.with_message(format!("invalid range for the jump: {from} > {to}"))
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::JumpTooBigInAlternation { limit } => Diagnostic::error()
.with_message(format!(
"jumps over {limit} not allowed inside alternations (|)",
))
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::JumpUnboundedInAlternation => Diagnostic::error()
.with_message("unbounded jumps not allowed inside alternations (|)")
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::ModifiersDuplicated { modifier_name } => Diagnostic::error()
.with_message(format!(
"string modifier {modifier_name} appears multiple times",
))
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::ModifiersIncompatible {
first_modifier_name,
second_modifier_name,
} => Diagnostic::error()
.with_message(format!(
"string modifiers {first_modifier_name} and {second_modifier_name} are incompatible",
))
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::MulOverflow { left, right } => Diagnostic::error()
.with_message(format!("multiplication {left} * {right} overflows"))
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::NomError(_) => Diagnostic::error()
.with_message("syntax error")
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::StrToIntError(err) => Diagnostic::error()
.with_message(format!("error converting to integer: {err}"))
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::StrToHexIntError(err) => Diagnostic::error()
.with_message(format!(
"error converting hexadecimal notation to integer: {err}"
))
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::StrToOctIntError(err) => Diagnostic::error()
.with_message(format!(
"error converting octal notation to integer: {err}"
))
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::XorRangeInvalidValue { value } => Diagnostic::error()
.with_message(format!(
"xor range value {value} invalid, must be in [0-255]"
))
.with_labels(vec![Label::primary((), self.span.clone())]),
ErrorKind::XorRangeInvalid { from, to } => Diagnostic::error()
.with_message(format!("xor range invalid: {from} > {to}"))
.with_labels(vec![Label::primary((), self.span.clone())]),
}
}
fn from_nom_error_kind(position: usize, kind: NomErrorKind) -> Self {
Self {
span: position..(position + 1),
kind: ErrorKind::NomError(kind),
}
}
}
impl ParseError<Input<'_>> for Error {
fn from_error_kind(input: Input, kind: NomErrorKind) -> Self {
Self::from_nom_error_kind(input.get_position_offset(), kind)
}
fn append(_: Input, _: NomErrorKind, other: Self) -> Self {
other
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum ErrorKind {
Base64AlphabetInvalidLength { length: usize },
Base64AlphabetIncompatible,
CannotNegateMaskAll,
ExprTooDeep,
HexStringTooDeep,
JumpAtBound,
JumpEmpty,
JumpRangeInvalid { from: u32, to: u32 },
JumpTooBigInAlternation {
limit: u32,
},
JumpUnboundedInAlternation,
ModifiersDuplicated {
modifier_name: String,
},
ModifiersIncompatible {
first_modifier_name: String,
second_modifier_name: String,
},
MulOverflow { left: i64, right: i64 },
NomError(NomErrorKind),
RegexClassRangeInvalid,
RegexNonAsciiByte,
RegexRangeInvalid,
RegexTooDeep,
StrToIntError(ParseIntError),
StrToHexIntError(ParseIntError),
StrToOctIntError(ParseIntError),
XorRangeInvalidValue { value: i64 },
XorRangeInvalid { from: u8, to: u8 },
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helpers::test_public_type;
#[test]
fn test_public_types() {
test_public_type(Error::new(0..3, ErrorKind::JumpEmpty));
}
}