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 #[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#[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 pub fn range(&self) -> Range {
149 self.0.range
150 }
151
152 pub fn line_display(&self) -> usize {
154 self.0.line_display
155 }
156
157 pub fn column_display(&self) -> usize {
163 self.0.column_display
164 }
165
166 pub fn kind(&self) -> &ParseErrorKind {
168 &self.0.kind
169 }
170
171 #[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 #[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}