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