|  | use std::char; | 
|  | use std::collections::HashMap; | 
|  | use std::fmt; | 
|  | use std::iter::Peekable; | 
|  | use std::str::FromStr; | 
|  |  | 
|  | use crate::JsonValue; | 
|  |  | 
|  | /// Parse error. | 
|  | /// | 
|  | /// ``` | 
|  | /// use tinyjson::{JsonParser, JsonParseError}; | 
|  | /// let error = JsonParser::new("[1, 2, 3".chars()).parse().unwrap_err(); | 
|  | /// assert!(matches!(error, JsonParseError{..})); | 
|  | /// ``` | 
|  | #[derive(Debug)] | 
|  | pub struct JsonParseError { | 
|  | msg: String, | 
|  | line: usize, | 
|  | col: usize, | 
|  | } | 
|  |  | 
|  | impl JsonParseError { | 
|  | fn new(msg: String, line: usize, col: usize) -> JsonParseError { | 
|  | JsonParseError { msg, line, col } | 
|  | } | 
|  |  | 
|  | /// Get the line numbr where the parse error happened. This value is 1-based. | 
|  | /// | 
|  | /// ``` | 
|  | /// use tinyjson::{JsonParser, JsonParseError}; | 
|  | /// let error = JsonParser::new("[1, 2, 3".chars()).parse().unwrap_err(); | 
|  | /// assert_eq!(error.line(), 1); | 
|  | /// ``` | 
|  | pub fn line(&self) -> usize { | 
|  | self.line | 
|  | } | 
|  |  | 
|  | /// Get the column numbr where the parse error happened. This value is 1-based. | 
|  | /// | 
|  | /// ``` | 
|  | /// use tinyjson::{JsonParser, JsonParseError}; | 
|  | /// let error = JsonParser::new("[1, 2, 3".chars()).parse().unwrap_err(); | 
|  | /// assert_eq!(error.column(), 8); | 
|  | /// ``` | 
|  | pub fn column(&self) -> usize { | 
|  | self.col | 
|  | } | 
|  | } | 
|  |  | 
|  | impl fmt::Display for JsonParseError { | 
|  | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 
|  | write!( | 
|  | f, | 
|  | "Parse error at line:{}, col:{}: {}", | 
|  | self.line, self.col, &self.msg, | 
|  | ) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl std::error::Error for JsonParseError {} | 
|  |  | 
|  | /// Convenient type alias for parse results. | 
|  | pub type JsonParseResult = Result<JsonValue, JsonParseError>; | 
|  |  | 
|  | // Note: char::is_ascii_whitespace is not available because some characters are not defined as | 
|  | // whitespace character in JSON spec. For example, U+000C FORM FEED is whitespace in Rust but | 
|  | // it isn't in JSON. | 
|  | fn is_whitespace(c: char) -> bool { | 
|  | match c { | 
|  | '\u{0020}' | '\u{000a}' | '\u{000d}' | '\u{0009}' => true, | 
|  | _ => false, | 
|  | } | 
|  | } | 
|  |  | 
|  | /// JSON parser to parse UTF-8 string into `JsonValue` value. | 
|  | /// | 
|  | /// Basically you don't need to use this struct directly thanks to `FromStr` trait implementation. | 
|  | /// | 
|  | /// ``` | 
|  | /// use tinyjson::{JsonParser, JsonValue}; | 
|  | /// | 
|  | /// let mut parser = JsonParser::new("[1, 2, 3]".chars()); | 
|  | /// let array = parser.parse().unwrap(); | 
|  | /// | 
|  | /// // Equivalent to the above code using `FromStr` | 
|  | /// let array: JsonValue = "[1, 2, 3]".parse().unwrap(); | 
|  | /// ``` | 
|  | pub struct JsonParser<I> | 
|  | where | 
|  | I: Iterator<Item = char>, | 
|  | { | 
|  | chars: Peekable<I>, | 
|  | line: usize, | 
|  | col: usize, | 
|  | } | 
|  |  | 
|  | impl<I: Iterator<Item = char>> JsonParser<I> { | 
|  | /// Create a new parser instance from an iterator which iterates characters. The iterator is usually built from | 
|  | /// `str::chars` for parsing `str` or `String` values. | 
|  | pub fn new(it: I) -> Self { | 
|  | JsonParser { | 
|  | chars: it.peekable(), | 
|  | line: 1, | 
|  | col: 0, | 
|  | } | 
|  | } | 
|  |  | 
|  | fn err<T>(&self, msg: String) -> Result<T, JsonParseError> { | 
|  | Err(JsonParseError::new(msg, self.line, self.col)) | 
|  | } | 
|  |  | 
|  | fn unexpected_eof(&self) -> Result<char, JsonParseError> { | 
|  | Err(JsonParseError::new( | 
|  | String::from("Unexpected EOF"), | 
|  | self.line, | 
|  | self.col, | 
|  | )) | 
|  | } | 
|  |  | 
|  | fn next_pos(&mut self, c: char) { | 
|  | if c == '\n' { | 
|  | self.col = 0; | 
|  | self.line += 1; | 
|  | } else { | 
|  | self.col += 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | fn peek(&mut self) -> Result<char, JsonParseError> { | 
|  | while let Some(c) = self.chars.peek().copied() { | 
|  | if !is_whitespace(c) { | 
|  | return Ok(c); | 
|  | } | 
|  | self.next_pos(c); | 
|  | self.chars.next().unwrap(); | 
|  | } | 
|  | self.unexpected_eof() | 
|  | } | 
|  |  | 
|  | fn next(&mut self) -> Option<char> { | 
|  | while let Some(c) = self.chars.next() { | 
|  | self.next_pos(c); | 
|  | if !is_whitespace(c) { | 
|  | return Some(c); | 
|  | } | 
|  | } | 
|  | None | 
|  | } | 
|  |  | 
|  | fn consume(&mut self) -> Result<char, JsonParseError> { | 
|  | if let Some(c) = self.next() { | 
|  | Ok(c) | 
|  | } else { | 
|  | self.unexpected_eof() | 
|  | } | 
|  | } | 
|  |  | 
|  | fn consume_no_skip(&mut self) -> Result<char, JsonParseError> { | 
|  | if let Some(c) = self.chars.next() { | 
|  | self.next_pos(c); | 
|  | Ok(c) | 
|  | } else { | 
|  | self.unexpected_eof() | 
|  | } | 
|  | } | 
|  |  | 
|  | fn parse_object(&mut self) -> JsonParseResult { | 
|  | if self.consume()? != '{' { | 
|  | return self.err(String::from("Object must starts with '{'")); | 
|  | } | 
|  |  | 
|  | if self.peek()? == '}' { | 
|  | self.consume().unwrap(); | 
|  | return Ok(JsonValue::Object(HashMap::new())); | 
|  | } | 
|  |  | 
|  | let mut m = HashMap::new(); | 
|  | loop { | 
|  | let key = match self.parse_any()? { | 
|  | JsonValue::String(s) => s, | 
|  | v => return self.err(format!("Key of object must be string but found {:?}", v)), | 
|  | }; | 
|  |  | 
|  | let c = self.consume()?; | 
|  | if c != ':' { | 
|  | return self.err(format!( | 
|  | "':' is expected after key of object but actually found '{}'", | 
|  | c | 
|  | )); | 
|  | } | 
|  |  | 
|  | m.insert(key, self.parse_any()?); | 
|  |  | 
|  | match self.consume()? { | 
|  | ',' => {} | 
|  | '}' => return Ok(JsonValue::Object(m)), | 
|  | c => { | 
|  | return self.err(format!( | 
|  | "',' or '}}' is expected for object but actually found '{}'", | 
|  | c.escape_debug(), | 
|  | )) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | fn parse_array(&mut self) -> JsonParseResult { | 
|  | if self.consume()? != '[' { | 
|  | return self.err(String::from("Array must starts with '['")); | 
|  | } | 
|  |  | 
|  | if self.peek()? == ']' { | 
|  | self.consume().unwrap(); | 
|  | return Ok(JsonValue::Array(vec![])); | 
|  | } | 
|  |  | 
|  | let mut v = vec![self.parse_any()?]; | 
|  | loop { | 
|  | match self.consume()? { | 
|  | ',' => {} | 
|  | ']' => return Ok(JsonValue::Array(v)), | 
|  | c => { | 
|  | return self.err(format!( | 
|  | "',' or ']' is expected for array but actually found '{}'", | 
|  | c | 
|  | )) | 
|  | } | 
|  | } | 
|  |  | 
|  | v.push(self.parse_any()?); // Next element | 
|  | } | 
|  | } | 
|  |  | 
|  | fn push_utf16(&self, s: &mut String, utf16: &mut Vec<u16>) -> Result<(), JsonParseError> { | 
|  | if utf16.is_empty() { | 
|  | return Ok(()); | 
|  | } | 
|  |  | 
|  | match String::from_utf16(utf16) { | 
|  | Ok(utf8) => s.push_str(&utf8), | 
|  | Err(err) => return self.err(format!("Invalid UTF-16 sequence {:?}: {}", &utf16, err)), | 
|  | } | 
|  | utf16.clear(); | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | fn parse_string(&mut self) -> JsonParseResult { | 
|  | if self.consume()? != '"' { | 
|  | return self.err(String::from("String must starts with double quote")); | 
|  | } | 
|  |  | 
|  | let mut utf16 = Vec::new(); // Buffer for parsing \uXXXX UTF-16 characters | 
|  | let mut s = String::new(); | 
|  | loop { | 
|  | let c = match self.consume_no_skip()? { | 
|  | '\\' => match self.consume_no_skip()? { | 
|  | '\\' => '\\', | 
|  | '/' => '/', | 
|  | '"' => '"', | 
|  | 'b' => '\u{0008}', | 
|  | 'f' => '\u{000c}', | 
|  | 'n' => '\n', | 
|  | 'r' => '\r', | 
|  | 't' => '\t', | 
|  | 'u' => { | 
|  | let mut u = 0u16; | 
|  | for _ in 0..4 { | 
|  | let c = self.consume()?; | 
|  | if let Some(h) = c.to_digit(16) { | 
|  | u = u * 0x10 + h as u16; | 
|  | } else { | 
|  | return self.err(format!("Unicode character must be \\uXXXX (X is hex character) format but found character '{}'", c)); | 
|  | } | 
|  | } | 
|  | utf16.push(u); | 
|  | // Additional \uXXXX character may follow. UTF-16 characters must be converted | 
|  | // into UTF-8 string as sequence because surrogate pairs must be considered | 
|  | // like "\uDBFF\uDFFF". | 
|  | continue; | 
|  | } | 
|  | c => return self.err(format!("'\\{}' is invalid escaped character", c)), | 
|  | }, | 
|  | '"' => { | 
|  | self.push_utf16(&mut s, &mut utf16)?; | 
|  | return Ok(JsonValue::String(s)); | 
|  | } | 
|  | // Note: c.is_control() is not available here because JSON accepts 0x7f (DEL) in | 
|  | // string literals but 0x7f is control character. | 
|  | // Rough spec of JSON says string literal cannot contain control characters. But it | 
|  | // can actually contain 0x7f. | 
|  | c if (c as u32) < 0x20 => { | 
|  | return self.err(format!( | 
|  | "String cannot contain control character {}", | 
|  | c.escape_debug(), | 
|  | )); | 
|  | } | 
|  | c => c, | 
|  | }; | 
|  |  | 
|  | self.push_utf16(&mut s, &mut utf16)?; | 
|  |  | 
|  | s.push(c); | 
|  | } | 
|  | } | 
|  |  | 
|  | fn parse_constant(&mut self, s: &'static str) -> Option<JsonParseError> { | 
|  | for c in s.chars() { | 
|  | match self.consume_no_skip() { | 
|  | Ok(x) if x != c => { | 
|  | return Some(JsonParseError::new( | 
|  | format!("Unexpected character '{}' while parsing '{}'", c, s), | 
|  | self.line, | 
|  | self.col, | 
|  | )); | 
|  | } | 
|  | Ok(_) => {} | 
|  | Err(e) => return Some(e), | 
|  | } | 
|  | } | 
|  | None | 
|  | } | 
|  |  | 
|  | fn parse_null(&mut self) -> JsonParseResult { | 
|  | match self.parse_constant("null") { | 
|  | Some(err) => Err(err), | 
|  | None => Ok(JsonValue::Null), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn parse_true(&mut self) -> JsonParseResult { | 
|  | match self.parse_constant("true") { | 
|  | Some(err) => Err(err), | 
|  | None => Ok(JsonValue::Boolean(true)), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn parse_false(&mut self) -> JsonParseResult { | 
|  | match self.parse_constant("false") { | 
|  | Some(err) => Err(err), | 
|  | None => Ok(JsonValue::Boolean(false)), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn parse_number(&mut self) -> JsonParseResult { | 
|  | let neg = if self.peek()? == '-' { | 
|  | self.consume_no_skip().unwrap(); | 
|  | true | 
|  | } else { | 
|  | false | 
|  | }; | 
|  |  | 
|  | let mut s = String::new(); | 
|  | let mut saw_dot = false; | 
|  | let mut saw_exp = false; | 
|  |  | 
|  | while let Some(d) = self.chars.peek() { | 
|  | match d { | 
|  | '0'..='9' => s.push(*d), | 
|  | '.' => { | 
|  | saw_dot = true; | 
|  | break; | 
|  | } | 
|  | 'e' | 'E' => { | 
|  | saw_exp = true; | 
|  | break; | 
|  | } | 
|  | _ => break, | 
|  | } | 
|  | self.consume_no_skip().unwrap(); | 
|  | } | 
|  |  | 
|  | if s.is_empty() { | 
|  | return self.err("Integer part must not be empty in number literal".to_string()); | 
|  | } | 
|  |  | 
|  | if s.starts_with('0') && s.len() > 1 { | 
|  | return self | 
|  | .err("Integer part of number must not start with 0 except for '0'".to_string()); | 
|  | } | 
|  |  | 
|  | if saw_dot { | 
|  | s.push(self.consume_no_skip().unwrap()); // eat '.' | 
|  | while let Some(d) = self.chars.peek() { | 
|  | match d { | 
|  | '0'..='9' => s.push(*d), | 
|  | 'e' | 'E' => { | 
|  | saw_exp = true; | 
|  | break; | 
|  | } | 
|  | _ => break, | 
|  | } | 
|  | self.consume_no_skip().unwrap(); | 
|  | } | 
|  | if s.ends_with('.') { | 
|  | return self.err("Fraction part of number must not be empty".to_string()); | 
|  | } | 
|  | } | 
|  |  | 
|  | if saw_exp { | 
|  | s.push(self.consume_no_skip().unwrap()); // eat 'e' or 'E' | 
|  | if let Some('+') | Some('-') = self.chars.peek() { | 
|  | s.push(self.consume_no_skip().unwrap()); | 
|  | } | 
|  |  | 
|  | let mut saw_digit = false; | 
|  | while let Some(d) = self.chars.peek() { | 
|  | match d { | 
|  | '0'..='9' => s.push(*d), | 
|  | _ => break, | 
|  | } | 
|  | saw_digit = true; | 
|  | self.consume_no_skip().unwrap(); | 
|  | } | 
|  |  | 
|  | if !saw_digit { | 
|  | return self.err("Exponent part must not be empty in number literal".to_string()); | 
|  | } | 
|  | } | 
|  |  | 
|  | match s.parse::<f64>() { | 
|  | Ok(n) => Ok(JsonValue::Number(if neg { -n } else { n })), | 
|  | Err(err) => self.err(format!("Invalid number literal '{}': {}", s, err)), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn parse_any(&mut self) -> JsonParseResult { | 
|  | match self.peek()? { | 
|  | '0'..='9' | '-' => self.parse_number(), | 
|  | '"' => self.parse_string(), | 
|  | '[' => self.parse_array(), | 
|  | '{' => self.parse_object(), | 
|  | 't' => self.parse_true(), | 
|  | 'f' => self.parse_false(), | 
|  | 'n' => self.parse_null(), | 
|  | c => self.err(format!("Invalid character: {}", c.escape_debug())), | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Run the parser to parse one JSON value. | 
|  | pub fn parse(&mut self) -> JsonParseResult { | 
|  | let v = self.parse_any()?; | 
|  |  | 
|  | if let Some(c) = self.next() { | 
|  | return self.err(format!( | 
|  | "Expected EOF but got character '{}'", | 
|  | c.escape_debug(), | 
|  | )); | 
|  | } | 
|  |  | 
|  | Ok(v) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Parse given `str` object into `JsonValue` value. This is recommended way to parse strings into JSON value with | 
|  | /// this library. | 
|  | /// | 
|  | /// ``` | 
|  | /// use tinyjson::JsonValue; | 
|  | /// | 
|  | /// let array: JsonValue = "[1, 2, 3]".parse().unwrap(); | 
|  | /// assert!(array.is_array()); | 
|  | /// ``` | 
|  | impl FromStr for JsonValue { | 
|  | type Err = JsonParseError; | 
|  |  | 
|  | fn from_str(s: &str) -> Result<Self, Self::Err> { | 
|  | JsonParser::new(s.chars()).parse() | 
|  | } | 
|  | } |