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