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