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#[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 pub fn range(&self) -> Range {
140 self.0.range
141 }
142
143 pub fn line_display(&self) -> usize {
145 self.0.line_display
146 }
147
148 pub fn column_display(&self) -> usize {
154 self.0.column_display
155 }
156
157 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}