Skip to content

Commit

Permalink
feat: improve error struct
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Oct 19, 2024
1 parent 3c9eab0 commit 924c55e
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 86 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ jobs:
run: |
cargo test --features serde
cargo test --features preserve_order
cargo test --verbose --all-features
cargo test --all-features
- name: Test release
if: matrix.config.kind == 'test_release'
run: cargo test --release --verbose --all-features
run: cargo test --release --all-features

# CARGO PUBLISH
- name: Cargo login
Expand All @@ -48,14 +48,14 @@ jobs:
name: Benchmarks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Install latest nightly
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
- name: Cache cargo
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ jobs:

steps:
- name: Clone repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
token: ${{ secrets.GH_DPRINTBOT_PAT }}

- uses: denoland/setup-deno@v1
- uses: denoland/setup-deno@v2
- uses: dsherret/rust-toolchain-file@v1

- name: Bump version and tag
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ all-features = true
[dependencies]
indexmap = { version = "2.2.6", optional = true }
serde_json = { version = "1.0", optional = true }
unicode-width = { version = "0.2.0", optional = true }

[features]
cst = []
preserve_order = ["indexmap", "serde_json/preserve_order"]
serde = ["serde_json"]
error_unicode_width = ["unicode-width"]

[dev-dependencies]
pretty_assertions = "1.0.0"
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2020-2021 David Sherret
Copyright (c) 2020 David Sherret

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
143 changes: 125 additions & 18 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,159 @@
use std::error::Error;
use std::fmt;

use crate::ParseStringErrorKind;

use super::common::Range;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ParseErrorKind {
CommentsNotAllowed,
ExpectedColonAfterObjectKey,
ExpectedObjectValue,
ExpectedDigit,
ExpectedDigitFollowingNegativeSign,
ExpectedPlusMinusOrDigitInNumberLiteral,
ExpectedStringObjectProperty,
MultipleRootJsonValues,
String(ParseStringErrorKind),
TrailingCommasNotAllowed,
UnexpectedCloseBrace,
UnexpectedCloseBracket,
UnexpectedColon,
UnexpectedComma,
UnexpectedToken,
UnexpectedTokenInObject,
UnexpectedWord,
UnterminatedArray,
UnterminatedCommentBlock,
UnterminatedObject,
}

impl std::fmt::Display for ParseErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use ParseErrorKind::*;
match self {
CommentsNotAllowed => {
write!(f, "Comments are not allowed")
}
ExpectedColonAfterObjectKey => {
write!(f, "Expected colon after the string or word in object property")
}
ExpectedDigit => {
write!(f, "Expected digit")
}
ExpectedDigitFollowingNegativeSign => {
write!(f, "Expected digit following negative sign")
}
ExpectedPlusMinusOrDigitInNumberLiteral => {
write!(f, "Expected plus, minus, or digit in number literal")
}
ExpectedObjectValue => {
write!(f, "Expected value after colon in object property")
}
ExpectedStringObjectProperty => {
write!(f, "Expected string for object property")
}
MultipleRootJsonValues => {
write!(f, "Text cannot contain more than one JSON value")
}
String(kind) => kind.fmt(f),
TrailingCommasNotAllowed => {
write!(f, "Trailing commas are not allowed")
}
UnexpectedCloseBrace => {
write!(f, "Unexpected close brace")
}
UnexpectedCloseBracket => {
write!(f, "Unexpected close bracket")
}
UnexpectedColon => {
write!(f, "Unexpected colon")
}
UnexpectedComma => {
write!(f, "Unexpected comma")
}
UnexpectedWord => {
write!(f, "Unexpected word")
}
UnexpectedToken => {
write!(f, "Unexpected token")
}
UnexpectedTokenInObject => {
write!(f, "Unexpected token in object")
}
UnterminatedArray => {
write!(f, "Unterminated array")
}
UnterminatedCommentBlock => {
write!(f, "Unterminated comment block")
}
UnterminatedObject => {
write!(f, "Unterminated object")
}
}
}
}

/// Error that could occur while parsing or tokenizing.
#[derive(Debug, PartialEq)]
pub struct ParseError {
/// Start and end position of the error.
pub range: Range,
/// 1-indexed line number the error occurred on.
pub line_display: usize,
/// 1-indexed column number the error occurred on.
///
/// Note: Use the `error_unicode_width` feature to get the correct column
/// number for Unicode characters on the line, otherwise this is just the
/// number of characters by default.
pub column_display: usize,
/// Error message.
pub message: String,
/// Message with the range text.
display_message: String,
pub kind: ParseErrorKind,
}

impl std::error::Error for ParseError {}

impl ParseError {
pub(crate) fn new(range: Range, message: &str, file_text: &str) -> ParseError {
let display_message = get_message_with_range(range, message, file_text);
pub(crate) fn new(range: Range, kind: ParseErrorKind, file_text: &str) -> ParseError {
let (line_display, column_display) = get_line_and_column_display(range, file_text);
ParseError {
message: message.to_string(),
range,
display_message,
line_display,
column_display,
kind,
}
}
}

impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.display_message)
}
}

impl Error for ParseError {
fn description(&self) -> &str {
&self.display_message
write!(
f,
"{} on line {} column {}",
self.kind, self.line_display, self.column_display
)
}
}

fn get_message_with_range(range: Range, message: &str, file_text: &str) -> String {
fn get_line_and_column_display(range: Range, file_text: &str) -> (usize, usize) {
let mut line_index = 0;
let mut column_index = 0;
for c in file_text[..range.start].chars() {
if c == '\n' {
line_index += 1;
column_index = 0;
} else {
column_index += 1;
#[cfg(feature = "error_unicode_width")]
{
if let Some(width) = unicode_width::UnicodeWidthChar::width_cjk(c) {
column_index += width;
}
}
#[cfg(not(feature = "error_unicode_width"))]
{
column_index += 1;
}
}
}
format!("{} on line {} column {}.", message, line_index + 1, column_index + 1,)
(line_index + 1, column_index + 1)
}
Loading

0 comments on commit 924c55e

Please sign in to comment.