Skip to main content

jsonc_parser/
errors.rs

1use std::fmt;
2
3use crate::ParseStringErrorKind;
4
5use super::common::Range;
6
7#[derive(Debug)]
8pub enum ParseErrorKind {
9  BareDecimalPointNumbersNotAllowed,
10  CommentsNotAllowed,
11  ExpectedColonAfterObjectKey,
12  ExpectedObjectValue,
13  ExpectedDigit,
14  ExpectedDigitFollowingNegativeSign,
15  ExpectedPlusMinusOrDigitInNumberLiteral,
16  ExpectedStringObjectProperty,
17  HexadecimalNumbersNotAllowed,
18  ExpectedComma,
19  MultipleRootJsonValues,
20  NonFiniteNumbersNotAllowed,
21  SingleQuotedStringsNotAllowed,
22  String(ParseStringErrorKind),
23  TrailingCommasNotAllowed,
24  UnaryPlusNumbersNotAllowed,
25  UnexpectedCloseBrace,
26  UnexpectedCloseBracket,
27  UnexpectedColon,
28  UnexpectedComma,
29  UnexpectedToken,
30  UnexpectedTokenInObject,
31  UnexpectedWord,
32  UnterminatedArray,
33  UnterminatedCommentBlock,
34  UnterminatedObject,
35  NestingDepthExceeded,
36  /// Custom error message, used by the serde deserializer.
37  #[cfg(feature = "serde")]
38  Custom(String),
39}
40
41impl std::fmt::Display for ParseErrorKind {
42  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43    use ParseErrorKind::*;
44    match self {
45      BareDecimalPointNumbersNotAllowed => {
46        write!(f, "Leading or trailing decimal points on numbers are not allowed")
47      }
48      CommentsNotAllowed => {
49        write!(f, "Comments are not allowed")
50      }
51      ExpectedColonAfterObjectKey => {
52        write!(f, "Expected colon after the string or word in object property")
53      }
54      ExpectedDigit => {
55        write!(f, "Expected digit")
56      }
57      ExpectedDigitFollowingNegativeSign => {
58        write!(f, "Expected digit following negative sign")
59      }
60      ExpectedPlusMinusOrDigitInNumberLiteral => {
61        write!(f, "Expected plus, minus, or digit in number literal")
62      }
63      ExpectedObjectValue => {
64        write!(f, "Expected value after colon in object property")
65      }
66      ExpectedStringObjectProperty => {
67        write!(f, "Expected string for object property")
68      }
69      HexadecimalNumbersNotAllowed => {
70        write!(f, "Hexadecimal numbers are not allowed")
71      }
72      ExpectedComma => {
73        write!(f, "Expected comma")
74      }
75      MultipleRootJsonValues => {
76        write!(f, "Text cannot contain more than one JSON value")
77      }
78      NonFiniteNumbersNotAllowed => {
79        write!(f, "Infinity and NaN are not allowed")
80      }
81      SingleQuotedStringsNotAllowed => {
82        write!(f, "Single-quoted strings are not allowed")
83      }
84      String(kind) => kind.fmt(f),
85      TrailingCommasNotAllowed => {
86        write!(f, "Trailing commas are not allowed")
87      }
88      UnaryPlusNumbersNotAllowed => {
89        write!(f, "Unary plus on numbers is not allowed")
90      }
91      UnexpectedCloseBrace => {
92        write!(f, "Unexpected close brace")
93      }
94      UnexpectedCloseBracket => {
95        write!(f, "Unexpected close bracket")
96      }
97      UnexpectedColon => {
98        write!(f, "Unexpected colon")
99      }
100      UnexpectedComma => {
101        write!(f, "Unexpected comma")
102      }
103      UnexpectedWord => {
104        write!(f, "Unexpected word")
105      }
106      UnexpectedToken => {
107        write!(f, "Unexpected token")
108      }
109      UnexpectedTokenInObject => {
110        write!(f, "Unexpected token in object")
111      }
112      UnterminatedArray => {
113        write!(f, "Unterminated array")
114      }
115      UnterminatedCommentBlock => {
116        write!(f, "Unterminated comment block")
117      }
118      UnterminatedObject => {
119        write!(f, "Unterminated object")
120      }
121      NestingDepthExceeded => {
122        write!(f, "Maximum nesting depth exceeded")
123      }
124      #[cfg(feature = "serde")]
125      Custom(msg) => write!(f, "{}", msg),
126    }
127  }
128}
129
130#[derive(Debug)]
131struct ParseErrorInner {
132  range: Range,
133  line_display: usize,
134  column_display: usize,
135  kind: ParseErrorKind,
136}
137
138/// Error that could occur while parsing or tokenizing.
139#[derive(Debug)]
140pub struct ParseError(Box<ParseErrorInner>);
141
142impl std::error::Error for ParseError {}
143
144impl ParseError {
145  pub(crate) fn new(range: Range, kind: ParseErrorKind, file_text: &str) -> ParseError {
146    let (line_display, column_display) = get_line_and_column_display(range, file_text);
147    ParseError(Box::new(ParseErrorInner {
148      range,
149      line_display,
150      column_display,
151      kind,
152    }))
153  }
154
155  /// Start and end position of the error.
156  pub fn range(&self) -> Range {
157    self.0.range
158  }
159
160  /// 1-indexed line number the error occurred on.
161  pub fn line_display(&self) -> usize {
162    self.0.line_display
163  }
164
165  /// 1-indexed column number the error occurred on.
166  ///
167  /// Note: Use the `error_unicode_width` feature to get the correct column
168  /// number for Unicode characters on the line, otherwise this is just the
169  /// number of characters by default.
170  pub fn column_display(&self) -> usize {
171    self.0.column_display
172  }
173
174  /// Error message.
175  pub fn kind(&self) -> &ParseErrorKind {
176    &self.0.kind
177  }
178
179  /// Creates an error with a custom message and no position info.
180  #[cfg(feature = "serde")]
181  pub(crate) fn custom_err(msg: String) -> ParseError {
182    ParseError(Box::new(ParseErrorInner {
183      range: Range { start: 0, end: 0 },
184      line_display: 1,
185      column_display: 1,
186      kind: ParseErrorKind::Custom(msg),
187    }))
188  }
189
190  /// Attaches position info to an error that doesn't have any yet.
191  #[cfg(feature = "serde")]
192  pub(crate) fn with_position(mut self, range: Range, file_text: &str) -> ParseError {
193    if self.0.range.start == 0 && self.0.range.end == 0 {
194      let (line_display, column_display) = get_line_and_column_display(range, file_text);
195      self.0.range = range;
196      self.0.line_display = line_display;
197      self.0.column_display = column_display;
198    }
199    self
200  }
201}
202
203impl fmt::Display for ParseError {
204  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205    let inner = &*self.0;
206    write!(
207      f,
208      "{} on line {} column {}",
209      inner.kind, inner.line_display, inner.column_display
210    )
211  }
212}
213
214fn get_line_and_column_display(range: Range, file_text: &str) -> (usize, usize) {
215  let mut line_index = 0;
216  let mut column_index = 0;
217  for (i, c) in file_text[..range.start].char_indices() {
218    // a lone \r ends a line comment, so it also counts as a line break here
219    if c == '\n' || c == '\r' && file_text.as_bytes().get(i + 1) != Some(&b'\n') {
220      line_index += 1;
221      column_index = 0;
222    } else {
223      #[cfg(feature = "error_unicode_width")]
224      {
225        if let Some(width) = unicode_width::UnicodeWidthChar::width_cjk(c) {
226          column_index += width;
227        }
228      }
229      #[cfg(not(feature = "error_unicode_width"))]
230      {
231        column_index += 1;
232      }
233    }
234  }
235  (line_index + 1, column_index + 1)
236}