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