jsonc_parser/
errors.rs

1use std::fmt;
2
3use crate::ParseStringErrorKind;
4
5use super::common::Range;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub enum ParseErrorKind {
9  CommentsNotAllowed,
10  ExpectedColonAfterObjectKey,
11  ExpectedObjectValue,
12  ExpectedDigit,
13  ExpectedDigitFollowingNegativeSign,
14  ExpectedPlusMinusOrDigitInNumberLiteral,
15  ExpectedStringObjectProperty,
16  MultipleRootJsonValues,
17  String(ParseStringErrorKind),
18  TrailingCommasNotAllowed,
19  UnexpectedCloseBrace,
20  UnexpectedCloseBracket,
21  UnexpectedColon,
22  UnexpectedComma,
23  UnexpectedToken,
24  UnexpectedTokenInObject,
25  UnexpectedWord,
26  UnterminatedArray,
27  UnterminatedCommentBlock,
28  UnterminatedObject,
29}
30
31impl std::fmt::Display for ParseErrorKind {
32  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33    use ParseErrorKind::*;
34    match self {
35      CommentsNotAllowed => {
36        write!(f, "Comments are not allowed")
37      }
38      ExpectedColonAfterObjectKey => {
39        write!(f, "Expected colon after the string or word in object property")
40      }
41      ExpectedDigit => {
42        write!(f, "Expected digit")
43      }
44      ExpectedDigitFollowingNegativeSign => {
45        write!(f, "Expected digit following negative sign")
46      }
47      ExpectedPlusMinusOrDigitInNumberLiteral => {
48        write!(f, "Expected plus, minus, or digit in number literal")
49      }
50      ExpectedObjectValue => {
51        write!(f, "Expected value after colon in object property")
52      }
53      ExpectedStringObjectProperty => {
54        write!(f, "Expected string for object property")
55      }
56      MultipleRootJsonValues => {
57        write!(f, "Text cannot contain more than one JSON value")
58      }
59      String(kind) => kind.fmt(f),
60      TrailingCommasNotAllowed => {
61        write!(f, "Trailing commas are not allowed")
62      }
63      UnexpectedCloseBrace => {
64        write!(f, "Unexpected close brace")
65      }
66      UnexpectedCloseBracket => {
67        write!(f, "Unexpected close bracket")
68      }
69      UnexpectedColon => {
70        write!(f, "Unexpected colon")
71      }
72      UnexpectedComma => {
73        write!(f, "Unexpected comma")
74      }
75      UnexpectedWord => {
76        write!(f, "Unexpected word")
77      }
78      UnexpectedToken => {
79        write!(f, "Unexpected token")
80      }
81      UnexpectedTokenInObject => {
82        write!(f, "Unexpected token in object")
83      }
84      UnterminatedArray => {
85        write!(f, "Unterminated array")
86      }
87      UnterminatedCommentBlock => {
88        write!(f, "Unterminated comment block")
89      }
90      UnterminatedObject => {
91        write!(f, "Unterminated object")
92      }
93    }
94  }
95}
96
97#[derive(Debug, Clone, PartialEq)]
98struct ParseErrorInner {
99  range: Range,
100  line_display: usize,
101  column_display: usize,
102  kind: ParseErrorKind,
103}
104
105/// Error that could occur while parsing or tokenizing.
106#[derive(Debug, Clone, PartialEq)]
107pub struct ParseError(Box<ParseErrorInner>);
108
109impl std::error::Error for ParseError {}
110
111impl ParseError {
112  pub(crate) fn new(range: Range, kind: ParseErrorKind, file_text: &str) -> ParseError {
113    let (line_display, column_display) = get_line_and_column_display(range, file_text);
114    ParseError(Box::new(ParseErrorInner {
115      range,
116      line_display,
117      column_display,
118      kind,
119    }))
120  }
121
122  /// Start and end position of the error.
123  pub fn range(&self) -> Range {
124    self.0.range
125  }
126
127  /// 1-indexed line number the error occurred on.
128  pub fn line_display(&self) -> usize {
129    self.0.line_display
130  }
131
132  /// 1-indexed column number the error occurred on.
133  ///
134  /// Note: Use the `error_unicode_width` feature to get the correct column
135  /// number for Unicode characters on the line, otherwise this is just the
136  /// number of characters by default.
137  pub fn column_display(&self) -> usize {
138    self.0.column_display
139  }
140
141  /// Error message.
142  pub fn kind(&self) -> &ParseErrorKind {
143    &self.0.kind
144  }
145}
146
147impl fmt::Display for ParseError {
148  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149    let inner = &*self.0;
150    write!(
151      f,
152      "{} on line {} column {}",
153      inner.kind, inner.line_display, inner.column_display
154    )
155  }
156}
157
158fn get_line_and_column_display(range: Range, file_text: &str) -> (usize, usize) {
159  let mut line_index = 0;
160  let mut column_index = 0;
161  for c in file_text[..range.start].chars() {
162    if c == '\n' {
163      line_index += 1;
164      column_index = 0;
165    } else {
166      #[cfg(feature = "error_unicode_width")]
167      {
168        if let Some(width) = unicode_width::UnicodeWidthChar::width_cjk(c) {
169          column_index += width;
170        }
171      }
172      #[cfg(not(feature = "error_unicode_width"))]
173      {
174        column_index += 1;
175      }
176    }
177  }
178  (line_index + 1, column_index + 1)
179}