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#[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 pub fn range(&self) -> Range {
144 self.0.range
145 }
146
147 pub fn line_display(&self) -> usize {
149 self.0.line_display
150 }
151
152 pub fn column_display(&self) -> usize {
158 self.0.column_display
159 }
160
161 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}