use crate::errors::syntax_error;
use crate::errors::unexpected_character_error;
use crate::errors::ParseResult;
use crate::numbers::NumberParser;
use crate::strings;
use crate::strings::StringParser;
use crate::strings::StringStandard;
use crate::Parser;
use std::collections::HashMap;
use std::fmt::Display;
use std::fmt::Formatter;
use std::mem;
#[derive(Debug, PartialEq, Clone, Default)]
pub enum JSON {
Object(HashMap<String, JSON>),
Array(Vec<JSON>),
String(String),
Number(f64),
Boolean(bool),
#[default]
Null,
}
impl Display for JSON {
fn fmt(&self, form: &mut Formatter) -> std::fmt::Result {
write!(form, "{}", json_to_string(self))
}
}
pub fn json_to_string(json: &JSON) -> String {
let mut strenc = strings::StringEncoder::new();
strenc.set(strings::StringStandard::JSON);
json_to_string_worker(json, &strenc, 0)
}
fn json_to_string_worker(json: &JSON, strenc: &strings::StringEncoder, indent: usize) -> String {
match json {
JSON::Object(map) => {
let mut first = true;
let mut buf = "{\n".to_string();
let mut keys: Vec<_> = map.keys().collect();
keys.sort_unstable();
buf.extend(std::iter::repeat(" ").take(indent + 1));
for key in keys {
if first {
first = false;
} else {
buf.push(',');
buf.push('\n');
buf.extend(std::iter::repeat(" ").take(indent + 1));
}
let value = map.get(key).unwrap();
buf.push_str(&encode_string(key, strenc));
buf.push(':');
buf.push(' ');
buf.push_str(&json_to_string_worker(value, strenc, indent + 1));
}
buf.push('\n');
buf.extend(std::iter::repeat(" ").take(indent));
buf.push('}');
buf
}
JSON::Array(array) => {
let mut first = true;
let mut buf = "[\n".to_string();
for _ in 0..indent + 1 {
buf.push_str(" ");
}
for value in array {
if first {
first = false;
} else {
buf.push(',');
buf.push('\n');
for _ in 0..indent + 1 {
buf.push_str(" ");
}
}
buf.push_str(&json_to_string_worker(value, strenc, indent + 1));
}
buf.push('\n');
for _ in 0..indent {
buf.push_str(" ");
}
buf.push(']');
buf
}
JSON::String(value) => encode_string(value, strenc),
JSON::Number(value) => value.to_string(),
JSON::Boolean(value) => value.to_string(),
JSON::Null => "null".to_string(),
}
}
fn encode_string(string: &str, strenc: &strings::StringEncoder) -> String {
let mut buf = "\"".to_string();
buf.push_str(&strenc.encode(string));
buf.push('"');
buf
}
#[derive(Debug)]
enum JSONContext {
Top,
InArray,
InObject,
}
pub struct JSONParser {
context_stack: Vec<JSONContext>,
array_stack: Vec<Vec<JSON>>,
object_stack: Vec<HashMap<String, JSON>>,
key_stack: Vec<String>,
context: JSONContext,
map: HashMap<String, JSON>,
array: Vec<JSON>,
key: String,
numpar: NumberParser,
strpar: StringParser,
}
impl JSONParser {
pub fn new() -> Self {
let mut strpar = StringParser::new();
strpar.set(StringStandard::JSON);
let mut numpar = NumberParser::new();
numpar.settings.permit_binary = false;
numpar.settings.permit_hexadecimal = false;
numpar.settings.permit_octal = false;
numpar.settings.permit_underscores = false;
numpar.settings.decimal_only_floats = true;
numpar.settings.permit_plus = false;
numpar.settings.permit_leading_zero = false;
numpar.settings.permit_empty_whole = false;
numpar.settings.permit_empty_fraction = false;
Self {
context_stack: Vec::with_capacity(10),
array_stack: Vec::with_capacity(10),
object_stack: Vec::with_capacity(10),
key_stack: Vec::with_capacity(10),
context: JSONContext::Top,
map: HashMap::new(),
array: vec![],
key: String::new(),
numpar,
strpar,
}
}
fn reset(&mut self) {
self.context_stack.clear();
self.array_stack.clear();
self.object_stack.clear();
self.key_stack.clear();
self.map.clear();
self.array.clear();
self.key = String::new();
self.context = JSONContext::Top;
}
fn push(&mut self, new_context: JSONContext) {
match self.context {
JSONContext::Top => {}
JSONContext::InArray => {
let array = mem::take(&mut self.array);
self.array_stack.push(array);
}
JSONContext::InObject => {
let map = mem::take(&mut self.map);
let key = mem::take(&mut self.key);
self.map = HashMap::new();
self.key = String::new();
self.key_stack.push(key);
self.object_stack.push(map);
}
}
self.context_stack
.push(mem::replace(&mut self.context, new_context));
}
#[cfg(not(tarpaulin_include))] fn pop(&mut self) {
self.context = match self.context_stack.pop() {
None => return,
Some(value) => value,
};
match self.context {
JSONContext::Top => {}
JSONContext::InArray => {
self.array = match self.array_stack.pop() {
None => panic!("Internal error: array stack is empty!"),
Some(value) => value,
};
}
JSONContext::InObject => {
self.map = match self.object_stack.pop() {
None => panic!("Internal error: object stack is empty!"),
Some(value) => value,
};
self.key = match self.key_stack.pop() {
None => panic!("Internal error: key stack is empty!"),
Some(value) => value,
};
}
}
}
pub fn parse_value_ws(&mut self, parser: &mut Parser) -> ParseResult<JSON> {
let allow_comments = parser.parse_comments;
parser.parse_comments = false;
let old_strpar = parser.replace_string_parser(self.strpar.clone());
let old_numpar = parser.replace_number_parser(self.numpar.clone());
let old_wspace = parser
.borrow_core()
.replace_whitespace_test(Box::new(|ch| [' ', '\n', '\r', '\t'].contains(&ch)));
parser.consume_ws();
let result = self.parse_value_worker_ws(parser);
parser.parse_comments = allow_comments;
let _ = parser.replace_string_parser(old_strpar);
let _ = parser.replace_number_parser(old_numpar);
let _ = parser.borrow_core().replace_whitespace_test(old_wspace);
self.reset();
result
}
pub fn parse_value_unstandard_ws(&mut self, parser: &mut Parser) -> ParseResult<JSON> {
self.reset();
parser.consume_ws();
self.parse_value_worker_ws(parser)
}
fn parse_value_worker_ws(&mut self, parser: &mut Parser) -> ParseResult<JSON> {
let mut optvalue = Some(JSON::Null);
let mut need_value = true;
loop {
if need_value {
if parser.is_at_eof() {
return Err(syntax_error(parser.loc(), "Unexpected end of file."));
}
optvalue = match parser.peek() {
'[' => {
parser.consume();
parser.consume_ws();
if parser.peek_and_consume_ws(']') {
Some(JSON::Array(vec![]))
} else {
self.push(JSONContext::InArray);
None
}
}
']' => {
return Err(syntax_error(
parser.loc(),
"Found a closing square bracket but expected a value that must be provided here."
));
}
'{' => {
parser.consume();
parser.consume_ws();
if parser.peek_and_consume_ws('}') {
Some(JSON::Object(HashMap::new()))
} else {
self.push(JSONContext::InObject);
if parser.peek_and_consume('"') {
self.key = parser.parse_string_until_delimiter_ws('"')?;
if parser.peek_and_consume_ws(':') {
None
} else {
return Err(syntax_error(
parser.loc(),
"Missing a colon following the key string.",
));
}
} else {
return Err(syntax_error(
parser.loc(),
"A string is required here for an object key, but was not found."
));
}
}
}
'}' => {
return Err(syntax_error(
parser.loc(),
"Found a closing brace but expected a value that must be provided here."
));
}
':' => {
return Err(syntax_error(
parser.loc(),
"Found a colon but expected a value that must be provided here.",
));
}
',' => {
return Err(syntax_error(
parser.loc(),
"Found a comma but expected a value that must be provided here.",
));
}
'"' => {
parser.consume();
Some(JSON::String(parser.parse_string_until_delimiter_ws('"')?))
}
'n' if parser.peek_and_consume_chars_ws(&['n', 'u', 'l', 'l']) => {
Some(JSON::Null)
}
't' if parser.peek_and_consume_chars_ws(&['t', 'r', 'u', 'e']) => {
Some(JSON::Boolean(true))
}
'f' if parser.peek_and_consume_chars_ws(&['f', 'a', 'l', 's', 'e']) => {
Some(JSON::Boolean(false))
}
ch if ch == '-' || ch.is_ascii_digit() => {
Some(JSON::Number(parser.parse_f64_decimal_ws()?))
}
ch => {
return Err(unexpected_character_error(parser.loc(), "a JSON value", ch));
}
};
}
if optvalue.is_none() {
continue;
}
need_value = true;
let value = optvalue.take().unwrap();
match self.context {
JSONContext::Top => {
return Ok(value);
}
JSONContext::InArray => {
self.array.push(value);
if parser.peek_and_consume_ws(',') {
continue;
} else if parser.peek_and_consume_ws(']') {
let array = mem::take(&mut self.array);
optvalue = Some(JSON::Array(array));
need_value = false;
self.pop();
continue;
} else {
return Err(unexpected_character_error(
parser.loc(),
"a comma and the next value or a closing bracket",
parser.peek(),
));
}
}
JSONContext::InObject => {
self.map.insert(mem::take(&mut self.key), value);
if parser.peek_and_consume_ws(',') {
if parser.peek_and_consume('"') {
self.key = parser.parse_string_until_delimiter_ws('"')?;
if parser.peek_and_consume_ws(':') {
continue;
} else {
return Err(syntax_error(
parser.loc(),
"Missing a colon following the key string.",
));
}
} else {
return Err(syntax_error(
parser.loc(),
"A string is required here for an object key, but was not found.",
));
}
} else if parser.peek_and_consume_ws('}') {
let map = mem::take(&mut self.map);
optvalue = Some(JSON::Object(map));
need_value = false;
self.pop();
continue;
} else {
return Err(unexpected_character_error(
parser.loc(),
"a comma and the next key and value pair or a closing brace",
parser.peek(),
));
}
}
};
}
}
}
impl Default for JSONParser {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod test {
use std::collections::HashMap;
use crate::{
parse_from_string, parsers::json::json_to_string, parsers::json::JSONParser,
parsers::json::JSON,
};
#[test]
fn simple_test() {
let mut jp = JSONParser::new();
let tests = &[
("", ""),
("{", ""),
("{}", "{\n \n}"),
("-154.2", "-154.2"),
("0", "0"),
(r#""This is a string.""#, r#""This is a string.""#),
(
r#""\nThis has \"stringiness\"""#,
r#""\nThis has \"stringiness\"""#,
),
("true", "true"),
("false", "false"),
("null", "null"),
(
"[true, true, false, true, false]",
"[\n true,\n true,\n false,\n true,\n false\n]",
),
(r#"{"Jimmy": "McGill"}"#, "{\n \"Jimmy\": \"McGill\"\n}"),
("[5 6]", ""),
("{\"tom\" \"jones\"}", ""),
("{\"tom\":\"jones\" \"glen\":\"campbell\"", ""),
("{joe:\"satriani\"}", ""),
("{\"joe\" \"satriani\"}", ""),
("{\"joe\":\"dart\",joe:\"satriani\"}", ""),
("{\"joe\":\"dart\",\"joe\" \"satriani\"}", ""),
(
"{\"tom\":[\"jones\",\"glen\",{\"campbell\":1.0}]}",
"{\n \"tom\": [\n \"jones\",\n \"glen\",\n {\n \"campbell\": 1\n }\n ]\n}"
),
("{\"tom\":}", ""),
("{\"tom\"}", ""),
("[,]", ""),
("[:]", ""),
("[}", ""),
("{]", ""),
("-12.5", "-12.5"),
("^", ""),
("[]", "[\n \n]"),
("{}", "{\n \n}"),
("{\"a\":}", ""),
("[\"a\",]", ""),
("{\"a\":-12}", "{\n \"a\": -12\n}"),
("[-12,-12]", "[\n -12,\n -12\n]"),
];
for (in_str, out_str) in tests {
println!("Testing {}", in_str);
let mut parser = parse_from_string(in_str);
if out_str.is_empty() {
assert!(jp.parse_value_ws(&mut parser).is_err());
} else {
let result = json_to_string(&jp.parse_value_ws(&mut parser).unwrap());
println!("Result {}", &result);
assert_eq!(result, *out_str);
}
}
}
#[test]
fn unstandard_test() {
println!();
let mut std_jp = JSONParser::default();
let mut unstd_jp = JSONParser::new();
let tests = &[
("1.21", "1.21", "1.21"),
("{\"gigawats\":1.21}", "{\n \"gigawats\": 1.21\n}", "{\n \"gigawats\": 1.21\n}"),
("\"Cory Wong\"", "\"Cory Wong\"", "\"Cory Wong\""),
(
"[\"High On You\", \"Diana\", \"Perfect Way\", \"1000 Julys\"]",
"[\n \"High On You\",\n \"Diana\",\n \"Perfect Way\",\n \"1000 Julys\"\n]",
"[\n \"High On You\",\n \"Diana\",\n \"Perfect Way\",\n \"1000 Julys\"\n]",
),
("\"Städ\"", "\"Städ\"", "\"Städ\""),
("//Comment\n12", "12", ""),
("\"St\\U{Combining Diaeresis}ad\"", "\"St\\\\U{Combining Diaeresis}ad\"", ""),
("\"St.\\u{20}Lucia\"", "\"St. Lucia\"", ""),
("\"St.\\x20Lucia\"", "\"St. Lucia\"", ""),
("5e3", "5000", "5000"),
("5e", "", ""),
];
for (in_str, unstd_str, std_str) in tests {
println!("Testing {}", in_str);
let mut parser = parse_from_string(in_str);
if unstd_str.is_empty() {
assert!(unstd_jp.parse_value_unstandard_ws(&mut parser).is_err());
} else {
let result =
json_to_string(&unstd_jp.parse_value_unstandard_ws(&mut parser).unwrap());
println!("Unstandard Result {}", &result);
assert_eq!(result, *unstd_str);
}
let mut parser = parse_from_string(in_str);
if std_str.is_empty() {
assert!(std_jp.parse_value_ws(&mut parser).is_err());
} else {
let result = json_to_string(&std_jp.parse_value_ws(&mut parser).unwrap());
println!("Standard Result {}", &result);
assert_eq!(result, *std_str);
}
}
}
#[test]
fn object_test() {
println!();
let mut jp = JSONParser::new();
let json = r#"{
"A": {
"name": ["Wexler", "Kim"],
"actor": ["Seehorn", "Rhea"],
"prison": false,
"age": 34
},
"B": {
"name": ["McGill", "Jimmy"],
"actor": ["Odenkirk", "Bob"],
"prison": true,
"age": 41
}
}"#;
let json2 = JSON::Object(HashMap::from([
(
"B".to_string(),
JSON::Object(HashMap::from([
(
"name".to_string(),
JSON::Array(vec![
JSON::String("McGill".to_string()),
JSON::String("Jimmy".to_string()),
]),
),
(
"actor".to_string(),
JSON::Array(vec![
JSON::String("Odenkirk".to_string()),
JSON::String("Bob".to_string()),
]),
),
("prison".to_string(), JSON::Boolean(true)),
("age".to_string(), JSON::Number(41.0)),
])),
),
(
"A".to_string(),
JSON::Object(HashMap::from([
(
"name".to_string(),
JSON::Array(vec![
JSON::String("Wexler".to_string()),
JSON::String("Kim".to_string()),
]),
),
(
"actor".to_string(),
JSON::Array(vec![
JSON::String("Seehorn".to_string()),
JSON::String("Rhea".to_string()),
]),
),
("prison".to_string(), JSON::Boolean(false)),
("age".to_string(), JSON::Number(34.0)),
])),
),
]));
let mut parser = parse_from_string(json);
let value1 = jp.parse_value_ws(&mut parser).unwrap();
parser = parse_from_string(&json_to_string(&value1));
let value2 = jp.parse_value_ws(&mut parser).unwrap();
parser = parse_from_string(&json_to_string(&value2));
let value3 = jp.parse_value_ws(&mut parser).unwrap();
assert_eq!(value1, value2);
assert_eq!(value2, value3);
assert_eq!(value3, json2);
}
}