use jsonette::{FoldingStyle, FormatOptions, JsonNode, LineEnding, Span, format, minify, parse};
use std::sync::Mutex;
static TEST_LOCK: Mutex<()> = Mutex::new(());
#[test]
fn test_format_options_default() {
let opts = FormatOptions::default();
assert_eq!(opts.use_tabs, false);
assert_eq!(opts.indent, 2);
assert_eq!(opts.line_ending, LineEnding::LF);
assert_eq!(opts.folding_style, FoldingStyle::Expanded);
assert_eq!(opts.sort_keys, false);
assert_eq!(opts.space_after_colon, true);
assert_eq!(opts.space_after_comma, true);
}
#[test]
fn test_json_node_helpers() {
let span = Span { start: 5, end: 10 };
let node_null = JsonNode::Null(span.clone());
assert_eq!(node_null.node_type(), "null");
assert_eq!(node_null.span(), span);
let node_bool = JsonNode::Bool(true, span.clone());
assert_eq!(node_bool.node_type(), "bool");
assert_eq!(node_bool.span(), span);
let node_num = JsonNode::Number(42.0, "42".to_string(), span.clone());
assert_eq!(node_num.node_type(), "number");
assert_eq!(node_num.span(), span);
let node_str = JsonNode::String("hello".to_string(), span.clone());
assert_eq!(node_str.node_type(), "string");
assert_eq!(node_str.span(), span);
let node_arr = JsonNode::Array(vec![], span.clone());
assert_eq!(node_arr.node_type(), "array");
assert_eq!(node_arr.span(), span);
let node_obj = JsonNode::Object(vec![], span.clone());
assert_eq!(node_obj.node_type(), "object");
assert_eq!(node_obj.span(), span);
}
#[test]
fn test_parse_null() {
let input = "null";
let res = parse(input).unwrap();
assert_eq!(res, JsonNode::Null(Span { start: 0, end: 4 }));
}
#[test]
fn test_parse_bool() {
let input = "true";
let res = parse(input).unwrap();
assert_eq!(res, JsonNode::Bool(true, Span { start: 0, end: 4 }));
let input_false = "false";
let res_false = parse(input_false).unwrap();
assert_eq!(res_false, JsonNode::Bool(false, Span { start: 0, end: 5 }));
}
#[test]
fn test_parse_number() {
let input = "123.45e-2";
let res = parse(input).unwrap();
assert_eq!(
res,
JsonNode::Number(1.2345, "123.45e-2".to_string(), Span { start: 0, end: 9 })
);
}
#[test]
fn test_parse_string() {
let input = "\"hello \\u263a world\"";
let res = parse(input).unwrap();
assert_eq!(
res,
JsonNode::String("hello ☺ world".to_string(), Span { start: 0, end: 20 })
);
}
#[test]
fn test_parse_array() {
let input = "[1, true, null, \"hello\"]";
let res = parse(input).unwrap();
if let JsonNode::Array(elements, span) = res {
assert_eq!(span, Span { start: 0, end: 24 });
assert_eq!(elements.len(), 4);
assert_eq!(
elements[0],
JsonNode::Number(1.0, "1".to_string(), Span { start: 1, end: 2 })
);
assert_eq!(elements[1], JsonNode::Bool(true, Span { start: 4, end: 8 }));
assert_eq!(elements[2], JsonNode::Null(Span { start: 10, end: 14 }));
assert_eq!(
elements[3],
JsonNode::String("hello".to_string(), Span { start: 16, end: 23 })
);
} else {
panic!("expected array");
}
}
#[test]
fn test_parse_object() {
let input = "{\"a\": 1, \"b\": [false]}";
let res = parse(input).unwrap();
if let JsonNode::Object(pairs, span) = res {
assert_eq!(span, Span { start: 0, end: 22 });
assert_eq!(pairs.len(), 2);
assert_eq!(pairs[0].key, "a");
assert_eq!(
pairs[0].value,
JsonNode::Number(1.0, "1".to_string(), Span { start: 6, end: 7 })
);
assert_eq!(pairs[1].key, "b");
if let JsonNode::Array(arr, _) = &pairs[1].value {
assert_eq!(arr[0], JsonNode::Bool(false, Span { start: 15, end: 20 }));
} else {
panic!("expected array");
}
} else {
panic!("expected object");
}
}
#[test]
fn test_parse_deeply_nested() {
let input = "{\"x\": {\"y\": {\"z\": [1]}}}";
let res = parse(input).unwrap();
assert_eq!(res.node_type(), "object");
}
#[test]
fn test_parse_invalid_json() {
let input = "{\"key\": [1, 2, ]}";
let res = parse(input);
assert!(res.is_err());
let errs = res.unwrap_err();
assert_eq!(errs.len(), 1);
assert!(errs[0].span.start > 0);
}
#[test]
fn test_parse_empty_string() {
let input = " ";
let res = parse(input);
assert!(res.is_err());
}
#[test]
fn test_parse_multibyte_error_span() {
let input = "\"é\"x";
let res = parse(input);
assert!(res.is_err());
let errs = res.unwrap_err();
assert_eq!(errs.len(), 1);
assert_eq!(errs[0].span.start, 4);
assert_eq!(errs[0].span.end, 5);
}
#[test]
fn test_parse_multibyte_multiline_error_span() {
let input = "{\"name\": \"😀\"}\n[1, 2, ]";
let res = parse(input);
assert!(res.is_err());
let errs = res.unwrap_err();
assert_eq!(errs.len(), 1);
assert_eq!(errs[0].span.start, 17);
}
#[test]
fn test_parse_multibyte_error_inside_object() {
let input = "{\n \"name\": \"😀\",\n \"x\"\n}";
let res = parse(input);
assert!(res.is_err());
let errs = res.unwrap_err();
assert_eq!(errs.len(), 1);
assert_eq!(errs[0].span.start, 26);
}
#[test]
fn test_api_format_and_minify() {
let _guard = TEST_LOCK.lock().unwrap();
fn eq_ignore_span(a: &JsonNode, b: &JsonNode) -> bool {
match (a, b) {
(JsonNode::Null(_), JsonNode::Null(_)) => true,
(JsonNode::Bool(x, _), JsonNode::Bool(y, _)) => x == y,
(JsonNode::Number(x, rx, _), JsonNode::Number(y, ry, _)) => x == y && rx == ry,
(JsonNode::String(x, _), JsonNode::String(y, _)) => x == y,
(JsonNode::Array(x, _), JsonNode::Array(y, _)) => {
if x.len() != y.len() {
return false;
}
for (elem_a, elem_b) in x.iter().zip(y.iter()) {
if !eq_ignore_span(elem_a, elem_b) {
return false;
}
}
true
}
(JsonNode::Object(x, _), JsonNode::Object(y, _)) => {
if x.len() != y.len() {
return false;
}
for (pair_a, pair_b) in x.iter().zip(y.iter()) {
if pair_a.key != pair_b.key || !eq_ignore_span(&pair_a.value, &pair_b.value) {
return false;
}
}
true
}
_ => false,
}
}
let input = r#"{
"name": "DevEtte",
"version": 1.0,
"unicode": "☺",
"nested": {
"list": [true, false, null]
}
}"#;
let parsed = parse(input).expect("parse input");
let original_settings = jsonette::get_settings();
let mut default_settings = original_settings;
default_settings.format = FormatOptions::default();
jsonette::update_settings(default_settings).expect("reset global settings");
let formatted = format(&parsed);
let parsed_formatted = parse(&formatted).expect("parse formatted");
assert!(eq_ignore_span(&parsed, &parsed_formatted));
let formatted_twice = format(&parsed_formatted);
assert_eq!(formatted, formatted_twice);
let minified = minify(&parsed);
assert_eq!(
minified,
r#"{"name":"DevEtte","version":1.0,"unicode":"☺","nested":{"list":[true,false,null]}}"#
);
let formatted_global = format(&parsed);
assert_eq!(formatted, formatted_global);
let mut new_settings = default_settings;
new_settings.format.folding_style = FoldingStyle::Compact;
jsonette::update_settings(new_settings).expect("update global settings");
let formatted_compact = format(&parsed);
assert!(formatted_compact.contains("\"list\": [true, false, null]"));
jsonette::update_settings(original_settings).expect("restore global settings");
}
#[test]
fn test_api_parser_options() {
let _guard = TEST_LOCK.lock().unwrap();
let original_settings = jsonette::get_settings();
let comment_json = r#"{
// This is a line comment
"key": "value", /* and a block comment */
"number": 42
}"#;
let mut settings_no_comments = original_settings;
settings_no_comments.parser.allow_comments = false;
jsonette::update_settings(settings_no_comments).unwrap();
assert!(parse(comment_json).is_err());
let mut settings_comments = original_settings;
settings_comments.parser.allow_comments = true;
jsonette::update_settings(settings_comments).unwrap();
let parsed_comments = parse(comment_json).expect("should parse comments");
assert_eq!(parsed_comments.node_type(), "object");
let trailing_comma_json = r#"{
"array": [1, 2, 3,],
"nested": {
"key": "val",
},
}"#;
let mut settings_no_trailing = original_settings;
settings_no_trailing.parser.allow_trailing_commas = false;
jsonette::update_settings(settings_no_trailing).unwrap();
assert!(parse(trailing_comma_json).is_err());
let mut settings_trailing = original_settings;
settings_trailing.parser.allow_trailing_commas = true;
jsonette::update_settings(settings_trailing).unwrap();
let parsed_trailing = parse(trailing_comma_json).expect("should parse trailing commas");
assert_eq!(parsed_trailing.node_type(), "object");
jsonette::update_settings(original_settings).unwrap();
}