#[derive(Debug, PartialEq, Eq, Clone)]
pub enum JsonErrorType {
FloatExpectingInt,
DuplicateKey(String),
InternalError(String),
EofWhileParsingList,
EofWhileParsingObject,
EofWhileParsingString,
EofWhileParsingValue,
ExpectedColon,
ExpectedListCommaOrEnd,
ExpectedObjectCommaOrEnd,
ExpectedSomeIdent,
ExpectedSomeValue,
InvalidEscape,
InvalidNumber,
NumberOutOfRange,
InvalidUnicodeCodePoint,
ControlCharacterWhileParsingString,
KeyMustBeAString,
LoneLeadingSurrogateInHexEscape,
TrailingComma,
TrailingCharacters,
UnexpectedEndOfHexEscape,
RecursionLimitExceeded,
}
impl std::fmt::Display for JsonErrorType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::FloatExpectingInt => f.write_str("float value was found where an int was expected"),
Self::DuplicateKey(s) => write!(f, "Detected duplicate key {s:?}"),
Self::InternalError(s) => write!(f, "Internal error: {s:?}"),
Self::EofWhileParsingList => f.write_str("EOF while parsing a list"),
Self::EofWhileParsingObject => f.write_str("EOF while parsing an object"),
Self::EofWhileParsingString => f.write_str("EOF while parsing a string"),
Self::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
Self::ExpectedColon => f.write_str("expected `:`"),
Self::ExpectedListCommaOrEnd => f.write_str("expected `,` or `]`"),
Self::ExpectedObjectCommaOrEnd => f.write_str("expected `,` or `}`"),
Self::ExpectedSomeIdent => f.write_str("expected ident"),
Self::ExpectedSomeValue => f.write_str("expected value"),
Self::InvalidEscape => f.write_str("invalid escape"),
Self::InvalidNumber => f.write_str("invalid number"),
Self::NumberOutOfRange => f.write_str("number out of range"),
Self::InvalidUnicodeCodePoint => f.write_str("invalid unicode code point"),
Self::ControlCharacterWhileParsingString => {
f.write_str("control character (\\u0000-\\u001F) found while parsing a string")
}
Self::KeyMustBeAString => f.write_str("key must be a string"),
Self::LoneLeadingSurrogateInHexEscape => f.write_str("lone leading surrogate in hex escape"),
Self::TrailingComma => f.write_str("trailing comma"),
Self::TrailingCharacters => f.write_str("trailing characters"),
Self::UnexpectedEndOfHexEscape => f.write_str("unexpected end of hex escape"),
Self::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
}
}
}
pub type JsonResult<T> = Result<T, JsonError>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct JsonError {
pub error_type: JsonErrorType,
pub index: usize,
}
impl JsonError {
pub(crate) fn new(error_type: JsonErrorType, index: usize) -> Self {
Self { error_type, index }
}
pub fn get_position(&self, json_data: &[u8]) -> LinePosition {
LinePosition::find(json_data, self.index)
}
pub fn description(&self, json_data: &[u8]) -> String {
let position = self.get_position(json_data);
format!("{} at {}", self.error_type, position)
}
pub(crate) fn allowed_if_partial(&self) -> bool {
matches!(
self.error_type,
JsonErrorType::EofWhileParsingList
| JsonErrorType::EofWhileParsingObject
| JsonErrorType::EofWhileParsingString
| JsonErrorType::EofWhileParsingValue
| JsonErrorType::ExpectedListCommaOrEnd
| JsonErrorType::ExpectedObjectCommaOrEnd
)
}
}
impl std::fmt::Display for JsonError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} at index {}", self.error_type, self.index)
}
}
impl std::error::Error for JsonError {}
macro_rules! json_error {
($error_type:ident, $index:expr) => {
crate::errors::JsonError::new(crate::errors::JsonErrorType::$error_type, $index)
};
}
pub(crate) use json_error;
macro_rules! json_err {
($error_type:ident, $index:expr) => {
Err(crate::errors::json_error!($error_type, $index))
};
}
use crate::Jiter;
pub(crate) use json_err;
pub(crate) const DEFAULT_RECURSION_LIMIT: u8 = 200;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum JsonType {
Null,
Bool,
Int,
Float,
String,
Array,
Object,
}
impl std::fmt::Display for JsonType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Null => f.write_str("null"),
Self::Bool => f.write_str("bool"),
Self::Int => f.write_str("int"),
Self::Float => f.write_str("float"),
Self::String => f.write_str("string"),
Self::Array => f.write_str("array"),
Self::Object => f.write_str("object"),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum JiterErrorType {
JsonError(JsonErrorType),
WrongType { expected: JsonType, actual: JsonType },
}
impl std::fmt::Display for JiterErrorType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::JsonError(error_type) => write!(f, "{error_type}"),
Self::WrongType { expected, actual } => {
write!(f, "expected {expected} but found {actual}")
}
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct JiterError {
pub error_type: JiterErrorType,
pub index: usize,
}
impl std::fmt::Display for JiterError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} at index {}", self.error_type, self.index)
}
}
impl std::error::Error for JiterError {}
impl JiterError {
pub(crate) fn new(error_type: JiterErrorType, index: usize) -> Self {
Self { error_type, index }
}
pub fn get_position(&self, jiter: &Jiter) -> LinePosition {
jiter.error_position(self.index)
}
pub fn description(&self, jiter: &Jiter) -> String {
let position = self.get_position(jiter);
format!("{} at {}", self.error_type, position)
}
pub(crate) fn wrong_type(expected: JsonType, actual: JsonType, index: usize) -> Self {
Self::new(JiterErrorType::WrongType { expected, actual }, index)
}
}
impl From<JsonError> for JiterError {
fn from(error: JsonError) -> Self {
Self {
error_type: JiterErrorType::JsonError(error.error_type),
index: error.index,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LinePosition {
pub line: usize,
pub column: usize,
}
impl std::fmt::Display for LinePosition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "line {} column {}", self.line, self.column)
}
}
impl LinePosition {
pub fn new(line: usize, column: usize) -> Self {
Self { line, column }
}
pub fn find(json_data: &[u8], find: usize) -> Self {
let mut line = 1;
let mut last_line_start = 0;
let mut index = 0;
while let Some(next) = json_data.get(index) {
if *next == b'\n' {
line += 1;
last_line_start = index + 1;
}
if index == find {
return Self {
line,
column: index + 1 - last_line_start,
};
}
index += 1;
}
Self {
line,
column: index.saturating_sub(last_line_start),
}
}
pub fn short(&self) -> String {
format!("{}:{}", self.line, self.column)
}
}