• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::char;
2 use std::collections::HashMap;
3 use std::fmt;
4 use std::iter::Peekable;
5 use std::str::FromStr;
6 
7 use crate::JsonValue;
8 
9 /// Parse error.
10 ///
11 /// ```
12 /// use tinyjson::{JsonParser, JsonParseError};
13 /// let error = JsonParser::new("[1, 2, 3".chars()).parse().unwrap_err();
14 /// assert!(matches!(error, JsonParseError{..}));
15 /// ```
16 #[derive(Debug)]
17 pub struct JsonParseError {
18     msg: String,
19     line: usize,
20     col: usize,
21 }
22 
23 impl JsonParseError {
new(msg: String, line: usize, col: usize) -> JsonParseError24     fn new(msg: String, line: usize, col: usize) -> JsonParseError {
25         JsonParseError { msg, line, col }
26     }
27 
28     /// Get the line numbr where the parse error happened. This value is 1-based.
29     ///
30     /// ```
31     /// use tinyjson::{JsonParser, JsonParseError};
32     /// let error = JsonParser::new("[1, 2, 3".chars()).parse().unwrap_err();
33     /// assert_eq!(error.line(), 1);
34     /// ```
line(&self) -> usize35     pub fn line(&self) -> usize {
36         self.line
37     }
38 
39     /// Get the column numbr where the parse error happened. This value is 1-based.
40     ///
41     /// ```
42     /// use tinyjson::{JsonParser, JsonParseError};
43     /// let error = JsonParser::new("[1, 2, 3".chars()).parse().unwrap_err();
44     /// assert_eq!(error.column(), 8);
45     /// ```
column(&self) -> usize46     pub fn column(&self) -> usize {
47         self.col
48     }
49 }
50 
51 impl fmt::Display for JsonParseError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result52     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53         write!(
54             f,
55             "Parse error at line:{}, col:{}: {}",
56             self.line, self.col, &self.msg,
57         )
58     }
59 }
60 
61 impl std::error::Error for JsonParseError {}
62 
63 /// Convenient type alias for parse results.
64 pub type JsonParseResult = Result<JsonValue, JsonParseError>;
65 
66 // Note: char::is_ascii_whitespace is not available because some characters are not defined as
67 // whitespace character in JSON spec. For example, U+000C FORM FEED is whitespace in Rust but
68 // it isn't in JSON.
is_whitespace(c: char) -> bool69 fn is_whitespace(c: char) -> bool {
70     match c {
71         '\u{0020}' | '\u{000a}' | '\u{000d}' | '\u{0009}' => true,
72         _ => false,
73     }
74 }
75 
76 /// JSON parser to parse UTF-8 string into `JsonValue` value.
77 ///
78 /// Basically you don't need to use this struct directly thanks to `FromStr` trait implementation.
79 ///
80 /// ```
81 /// use tinyjson::{JsonParser, JsonValue};
82 ///
83 /// let mut parser = JsonParser::new("[1, 2, 3]".chars());
84 /// let array = parser.parse().unwrap();
85 ///
86 /// // Equivalent to the above code using `FromStr`
87 /// let array: JsonValue = "[1, 2, 3]".parse().unwrap();
88 /// ```
89 pub struct JsonParser<I>
90 where
91     I: Iterator<Item = char>,
92 {
93     chars: Peekable<I>,
94     line: usize,
95     col: usize,
96 }
97 
98 impl<I: Iterator<Item = char>> JsonParser<I> {
99     /// Create a new parser instance from an iterator which iterates characters. The iterator is usually built from
100     /// `str::chars` for parsing `str` or `String` values.
new(it: I) -> Self101     pub fn new(it: I) -> Self {
102         JsonParser {
103             chars: it.peekable(),
104             line: 1,
105             col: 0,
106         }
107     }
108 
err<T>(&self, msg: String) -> Result<T, JsonParseError>109     fn err<T>(&self, msg: String) -> Result<T, JsonParseError> {
110         Err(JsonParseError::new(msg, self.line, self.col))
111     }
112 
unexpected_eof(&self) -> Result<char, JsonParseError>113     fn unexpected_eof(&self) -> Result<char, JsonParseError> {
114         Err(JsonParseError::new(
115             String::from("Unexpected EOF"),
116             self.line,
117             self.col,
118         ))
119     }
120 
next_pos(&mut self, c: char)121     fn next_pos(&mut self, c: char) {
122         if c == '\n' {
123             self.col = 0;
124             self.line += 1;
125         } else {
126             self.col += 1;
127         }
128     }
129 
peek(&mut self) -> Result<char, JsonParseError>130     fn peek(&mut self) -> Result<char, JsonParseError> {
131         while let Some(c) = self.chars.peek().copied() {
132             if !is_whitespace(c) {
133                 return Ok(c);
134             }
135             self.next_pos(c);
136             self.chars.next().unwrap();
137         }
138         self.unexpected_eof()
139     }
140 
next(&mut self) -> Option<char>141     fn next(&mut self) -> Option<char> {
142         while let Some(c) = self.chars.next() {
143             self.next_pos(c);
144             if !is_whitespace(c) {
145                 return Some(c);
146             }
147         }
148         None
149     }
150 
consume(&mut self) -> Result<char, JsonParseError>151     fn consume(&mut self) -> Result<char, JsonParseError> {
152         if let Some(c) = self.next() {
153             Ok(c)
154         } else {
155             self.unexpected_eof()
156         }
157     }
158 
consume_no_skip(&mut self) -> Result<char, JsonParseError>159     fn consume_no_skip(&mut self) -> Result<char, JsonParseError> {
160         if let Some(c) = self.chars.next() {
161             self.next_pos(c);
162             Ok(c)
163         } else {
164             self.unexpected_eof()
165         }
166     }
167 
parse_object(&mut self) -> JsonParseResult168     fn parse_object(&mut self) -> JsonParseResult {
169         if self.consume()? != '{' {
170             return self.err(String::from("Object must starts with '{'"));
171         }
172 
173         if self.peek()? == '}' {
174             self.consume().unwrap();
175             return Ok(JsonValue::Object(HashMap::new()));
176         }
177 
178         let mut m = HashMap::new();
179         loop {
180             let key = match self.parse_any()? {
181                 JsonValue::String(s) => s,
182                 v => return self.err(format!("Key of object must be string but found {:?}", v)),
183             };
184 
185             let c = self.consume()?;
186             if c != ':' {
187                 return self.err(format!(
188                     "':' is expected after key of object but actually found '{}'",
189                     c
190                 ));
191             }
192 
193             m.insert(key, self.parse_any()?);
194 
195             match self.consume()? {
196                 ',' => {}
197                 '}' => return Ok(JsonValue::Object(m)),
198                 c => {
199                     return self.err(format!(
200                         "',' or '}}' is expected for object but actually found '{}'",
201                         c.escape_debug(),
202                     ))
203                 }
204             }
205         }
206     }
207 
parse_array(&mut self) -> JsonParseResult208     fn parse_array(&mut self) -> JsonParseResult {
209         if self.consume()? != '[' {
210             return self.err(String::from("Array must starts with '['"));
211         }
212 
213         if self.peek()? == ']' {
214             self.consume().unwrap();
215             return Ok(JsonValue::Array(vec![]));
216         }
217 
218         let mut v = vec![self.parse_any()?];
219         loop {
220             match self.consume()? {
221                 ',' => {}
222                 ']' => return Ok(JsonValue::Array(v)),
223                 c => {
224                     return self.err(format!(
225                         "',' or ']' is expected for array but actually found '{}'",
226                         c
227                     ))
228                 }
229             }
230 
231             v.push(self.parse_any()?); // Next element
232         }
233     }
234 
push_utf16(&self, s: &mut String, utf16: &mut Vec<u16>) -> Result<(), JsonParseError>235     fn push_utf16(&self, s: &mut String, utf16: &mut Vec<u16>) -> Result<(), JsonParseError> {
236         if utf16.is_empty() {
237             return Ok(());
238         }
239 
240         match String::from_utf16(utf16) {
241             Ok(utf8) => s.push_str(&utf8),
242             Err(err) => return self.err(format!("Invalid UTF-16 sequence {:?}: {}", &utf16, err)),
243         }
244         utf16.clear();
245         Ok(())
246     }
247 
parse_string(&mut self) -> JsonParseResult248     fn parse_string(&mut self) -> JsonParseResult {
249         if self.consume()? != '"' {
250             return self.err(String::from("String must starts with double quote"));
251         }
252 
253         let mut utf16 = Vec::new(); // Buffer for parsing \uXXXX UTF-16 characters
254         let mut s = String::new();
255         loop {
256             let c = match self.consume_no_skip()? {
257                 '\\' => match self.consume_no_skip()? {
258                     '\\' => '\\',
259                     '/' => '/',
260                     '"' => '"',
261                     'b' => '\u{0008}',
262                     'f' => '\u{000c}',
263                     'n' => '\n',
264                     'r' => '\r',
265                     't' => '\t',
266                     'u' => {
267                         let mut u = 0u16;
268                         for _ in 0..4 {
269                             let c = self.consume()?;
270                             if let Some(h) = c.to_digit(16) {
271                                 u = u * 0x10 + h as u16;
272                             } else {
273                                 return self.err(format!("Unicode character must be \\uXXXX (X is hex character) format but found character '{}'", c));
274                             }
275                         }
276                         utf16.push(u);
277                         // Additional \uXXXX character may follow. UTF-16 characters must be converted
278                         // into UTF-8 string as sequence because surrogate pairs must be considered
279                         // like "\uDBFF\uDFFF".
280                         continue;
281                     }
282                     c => return self.err(format!("'\\{}' is invalid escaped character", c)),
283                 },
284                 '"' => {
285                     self.push_utf16(&mut s, &mut utf16)?;
286                     return Ok(JsonValue::String(s));
287                 }
288                 // Note: c.is_control() is not available here because JSON accepts 0x7f (DEL) in
289                 // string literals but 0x7f is control character.
290                 // Rough spec of JSON says string literal cannot contain control characters. But it
291                 // can actually contain 0x7f.
292                 c if (c as u32) < 0x20 => {
293                     return self.err(format!(
294                         "String cannot contain control character {}",
295                         c.escape_debug(),
296                     ));
297                 }
298                 c => c,
299             };
300 
301             self.push_utf16(&mut s, &mut utf16)?;
302 
303             s.push(c);
304         }
305     }
306 
parse_constant(&mut self, s: &'static str) -> Option<JsonParseError>307     fn parse_constant(&mut self, s: &'static str) -> Option<JsonParseError> {
308         for c in s.chars() {
309             match self.consume_no_skip() {
310                 Ok(x) if x != c => {
311                     return Some(JsonParseError::new(
312                         format!("Unexpected character '{}' while parsing '{}'", c, s),
313                         self.line,
314                         self.col,
315                     ));
316                 }
317                 Ok(_) => {}
318                 Err(e) => return Some(e),
319             }
320         }
321         None
322     }
323 
parse_null(&mut self) -> JsonParseResult324     fn parse_null(&mut self) -> JsonParseResult {
325         match self.parse_constant("null") {
326             Some(err) => Err(err),
327             None => Ok(JsonValue::Null),
328         }
329     }
330 
parse_true(&mut self) -> JsonParseResult331     fn parse_true(&mut self) -> JsonParseResult {
332         match self.parse_constant("true") {
333             Some(err) => Err(err),
334             None => Ok(JsonValue::Boolean(true)),
335         }
336     }
337 
parse_false(&mut self) -> JsonParseResult338     fn parse_false(&mut self) -> JsonParseResult {
339         match self.parse_constant("false") {
340             Some(err) => Err(err),
341             None => Ok(JsonValue::Boolean(false)),
342         }
343     }
344 
parse_number(&mut self) -> JsonParseResult345     fn parse_number(&mut self) -> JsonParseResult {
346         let neg = if self.peek()? == '-' {
347             self.consume_no_skip().unwrap();
348             true
349         } else {
350             false
351         };
352 
353         let mut s = String::new();
354         let mut saw_dot = false;
355         let mut saw_exp = false;
356 
357         while let Some(d) = self.chars.peek() {
358             match d {
359                 '0'..='9' => s.push(*d),
360                 '.' => {
361                     saw_dot = true;
362                     break;
363                 }
364                 'e' | 'E' => {
365                     saw_exp = true;
366                     break;
367                 }
368                 _ => break,
369             }
370             self.consume_no_skip().unwrap();
371         }
372 
373         if s.is_empty() {
374             return self.err("Integer part must not be empty in number literal".to_string());
375         }
376 
377         if s.starts_with('0') && s.len() > 1 {
378             return self
379                 .err("Integer part of number must not start with 0 except for '0'".to_string());
380         }
381 
382         if saw_dot {
383             s.push(self.consume_no_skip().unwrap()); // eat '.'
384             while let Some(d) = self.chars.peek() {
385                 match d {
386                     '0'..='9' => s.push(*d),
387                     'e' | 'E' => {
388                         saw_exp = true;
389                         break;
390                     }
391                     _ => break,
392                 }
393                 self.consume_no_skip().unwrap();
394             }
395             if s.ends_with('.') {
396                 return self.err("Fraction part of number must not be empty".to_string());
397             }
398         }
399 
400         if saw_exp {
401             s.push(self.consume_no_skip().unwrap()); // eat 'e' or 'E'
402             if let Some('+') | Some('-') = self.chars.peek() {
403                 s.push(self.consume_no_skip().unwrap());
404             }
405 
406             let mut saw_digit = false;
407             while let Some(d) = self.chars.peek() {
408                 match d {
409                     '0'..='9' => s.push(*d),
410                     _ => break,
411                 }
412                 saw_digit = true;
413                 self.consume_no_skip().unwrap();
414             }
415 
416             if !saw_digit {
417                 return self.err("Exponent part must not be empty in number literal".to_string());
418             }
419         }
420 
421         match s.parse::<f64>() {
422             Ok(n) => Ok(JsonValue::Number(if neg { -n } else { n })),
423             Err(err) => self.err(format!("Invalid number literal '{}': {}", s, err)),
424         }
425     }
426 
parse_any(&mut self) -> JsonParseResult427     fn parse_any(&mut self) -> JsonParseResult {
428         match self.peek()? {
429             '0'..='9' | '-' => self.parse_number(),
430             '"' => self.parse_string(),
431             '[' => self.parse_array(),
432             '{' => self.parse_object(),
433             't' => self.parse_true(),
434             'f' => self.parse_false(),
435             'n' => self.parse_null(),
436             c => self.err(format!("Invalid character: {}", c.escape_debug())),
437         }
438     }
439 
440     /// Run the parser to parse one JSON value.
parse(&mut self) -> JsonParseResult441     pub fn parse(&mut self) -> JsonParseResult {
442         let v = self.parse_any()?;
443 
444         if let Some(c) = self.next() {
445             return self.err(format!(
446                 "Expected EOF but got character '{}'",
447                 c.escape_debug(),
448             ));
449         }
450 
451         Ok(v)
452     }
453 }
454 
455 /// Parse given `str` object into `JsonValue` value. This is recommended way to parse strings into JSON value with
456 /// this library.
457 ///
458 /// ```
459 /// use tinyjson::JsonValue;
460 ///
461 /// let array: JsonValue = "[1, 2, 3]".parse().unwrap();
462 /// assert!(array.is_array());
463 /// ```
464 impl FromStr for JsonValue {
465     type Err = JsonParseError;
466 
from_str(s: &str) -> Result<Self, Self::Err>467     fn from_str(s: &str) -> Result<Self, Self::Err> {
468         JsonParser::new(s.chars()).parse()
469     }
470 }
471