Skip to main content

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