#![cfg(feature = "json")]
mod common;
use common::manifest_dir;
use googletest::assert_that;
use googletest::matchers::container_eq;
use googletest::matchers::none;
use googletest::prelude::eq;
use googletest::prelude::some;
use insta::assert_compact_json_snapshot;
use serde_json::json;
use spath::NodeList;
use spath::SPath;
fn json_testdata(filename: &str) -> serde_json::Value {
let path = manifest_dir().join("testdata").join(filename);
let content = std::fs::read_to_string(path).unwrap();
serde_json::from_str(&content).unwrap()
}
fn eval_spath<'a>(
spath: &str,
value: &'a serde_json::Value,
) -> Result<NodeList<'a, serde_json::Value>, spath::ParseError> {
let registry = spath::json::BuiltinFunctionRegistry::default();
let spath = SPath::parse_with_registry(spath, registry)?;
Ok(spath.query(value))
}
#[test]
fn test_root_identical() {
let value = json_testdata("rfc-9535-example-1.json");
let result = eval_spath("$", &value).unwrap();
let result = result.exactly_one().unwrap();
assert_that!(result, eq(&value));
}
#[test]
fn test_basic_name_selector() {
let value = json_testdata("rfc-9535-example-1.json");
let result = eval_spath(r#"$["store"]['bicycle']"#, &value).unwrap();
let result = result.exactly_one().unwrap();
assert_compact_json_snapshot!(result, @r#"{"color": "red", "price": 399}"#);
let result = eval_spath(r#"$.store.bicycle.color"#, &value).unwrap();
let result = result.exactly_one().unwrap();
assert_compact_json_snapshot!(result, @r#""red""#);
let result = eval_spath(r#"$.store.book.*"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"
[
{
"author": "Nigel Rees",
"category": "reference",
"price": 8.95,
"title": "Sayings of the Century"
},
{
"author": "Evelyn Waugh",
"category": "fiction",
"price": 12.99,
"title": "Sword of Honour"
},
{
"author": "Herman Melville",
"category": "fiction",
"isbn": "0-553-21311-3",
"price": 8.99,
"title": "Moby Dick"
},
{
"author": "J. R. R. Tolkien",
"category": "fiction",
"isbn": "0-395-19395-8",
"price": 22.99,
"title": "The Lord of the Rings"
}
]
"#);
let value = json_testdata("rfc-9535-example-2.json");
let result = eval_spath(r#"$.o['j j']"#, &value).unwrap();
let result = result.exactly_one().unwrap();
assert_compact_json_snapshot!(result, @r#"{"k.k": 3}"#);
let result = eval_spath(r#"$.o['j j']['k.k'] "#, &value).unwrap();
let result = result.exactly_one().unwrap();
assert_compact_json_snapshot!(result, @"3");
let result = eval_spath(r#"$.o["j j"]["k.k"]"#, &value).unwrap();
let result = result.exactly_one().unwrap();
assert_compact_json_snapshot!(result, @"3");
let result = eval_spath(r#"$["'"]["@"]"#, &value).unwrap();
let result = result.exactly_one().unwrap();
assert_compact_json_snapshot!(result, @"2");
}
#[test]
fn test_basic_wildcard_selector() {
let value = json_testdata("rfc-9535-example-3.json");
let result = eval_spath(r#"$[*]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"[[5, 3], {"j": 1, "k": 2}]"#);
let result = eval_spath(r#"$.o[*]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @"[1, 2]");
let result = eval_spath(r#"$.o[*, *]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @"[1, 2, 1, 2]");
let result = eval_spath(r#"$.a[*]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @"[5, 3]");
}
#[test]
fn test_basic_index_slice_selector() {
let value = json_testdata("rfc-9535-example-4.json");
let result = eval_spath(r#"$[1]"#, &value).unwrap();
let result = result.exactly_one().unwrap();
assert_compact_json_snapshot!(result, @r#""b""#);
let result = eval_spath(r#"$[0]"#, &value).unwrap();
let result = result.exactly_one().unwrap();
assert_compact_json_snapshot!(result, @r#""a""#);
}
#[test]
fn test_basic_array_slice_selector() {
let value = json_testdata("rfc-9535-example-5.json");
let result = eval_spath(r#"$[1:3]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"["b", "c"]"#);
let result = eval_spath(r#"$[5:]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"["f", "g"]"#);
let result = eval_spath(r#"$[1:5:2]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"["b", "d"]"#);
let result = eval_spath(r#"$[5:1:-2]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"["f", "d"]"#);
let result = eval_spath(r#"$[::-1]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"["g", "f", "e", "d", "c", "b", "a"]"#);
}
#[test]
fn test_basic_child_and_descendant_segment() {
let value = json_testdata("rfc-9535-example-8.json");
let result = eval_spath(r#"$[0, 3]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"["a", "d"]"#);
let result = eval_spath(r#"$[0:2, 5]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"["a", "b", "f"]"#);
let result = eval_spath(r#"$[0,0]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"["a", "a"]"#);
let value = json_testdata("rfc-9535-example-9.json");
let result = eval_spath(r#"$..j"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @"[4, 1]");
let result = eval_spath(r#"$..[0]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"[5, {"j": 4}]"#);
let result = eval_spath(r#"$..*"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"[[5, 3, [{"j": 4}, {"k": 6}]], {"j": 1, "k": 2}, 5, 3, [{"j": 4}, {"k": 6}], {"j": 4}, {"k": 6}, 4, 6, 1, 2]"#);
let result = eval_spath(r#"$..[*]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"[[5, 3, [{"j": 4}, {"k": 6}]], {"j": 1, "k": 2}, 5, 3, [{"j": 4}, {"k": 6}], {"j": 4}, {"k": 6}, 4, 6, 1, 2]"#);
let result = eval_spath(r#"$..o"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"[{"j": 1, "k": 2}]"#);
let result = eval_spath(r#"$.o..[*, *]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @"[1, 2, 1, 2]");
let result = eval_spath(r#"$.a..[0, 1]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"[5, 3, {"j": 4}, {"k": 6}]"#);
}
#[test]
fn test_basic_null_semantic() {
let value = json_testdata("rfc-9535-example-10.json");
let null = serde_json::Value::Null;
let array_of_null = [null.clone()];
let value_of_ident_null = serde_json::Value::Number(1i64.into());
let result = eval_spath(r#"$.a"#, &value).unwrap();
assert_that!(result.at_most_one().unwrap(), some(eq(&null)));
let result = eval_spath(r#"$.a[0]"#, &value).unwrap();
assert_that!(result.at_most_one().unwrap(), none());
let result = eval_spath(r#"$.a.d"#, &value).unwrap();
assert_that!(result.at_most_one().unwrap(), none());
let result = eval_spath(r#"$.b[0]"#, &value).unwrap();
assert_that!(result.at_most_one().unwrap(), some(eq(&null)));
let result = eval_spath(r#"$.b[*]"#, &value).unwrap();
assert_that!(
result.all(),
container_eq(array_of_null.iter().collect::<Vec<_>>())
);
let result = eval_spath(r#"$.null"#, &value).unwrap();
assert_that!(
result.at_most_one().unwrap(),
some(eq(&value_of_ident_null))
);
}
#[test]
fn test_filters() {
let value = json! {{
"a": [3, 5, 1, 2, 4, 6,
{"b": "j"},
{"b": "k"},
{"b": {}},
{"b": "kilo"}
],
"o": {"p": 1, "q": 2, "r": 3, "s": 5, "t": {"u": 6}},
"e": "f"
}};
let result = eval_spath(r#"$.a[?@.b == 'kilo']"#, &value).unwrap();
let result = result.exactly_one().unwrap();
assert_compact_json_snapshot!(result, @r#"{"b": "kilo"}"#);
let result = eval_spath(r#"$.a[?(@.b == 'kilo')]"#, &value).unwrap();
let result = result.exactly_one().unwrap();
assert_compact_json_snapshot!(result, @r#"{"b": "kilo"}"#);
let result = eval_spath(r#"$.a[?@>3.5]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"[5, 4, 6]"#);
let result = eval_spath(r#"$.a[?@.b]"#, &value).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"[{"b": "j"}, {"b": "k"}, {"b": {}}, {"b": "kilo"}]"#);
}
#[test]
fn test_filter_functions() {
let values = json! {{
"a": [1, 2, 3, 4, 5],
"b": 42,
"c": [],
"d": {"e": 1, "f": 2},
}};
let result = eval_spath(r#"$[?length(@) < 3]"#, &values).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"[[], {"e": 1, "f": 2}]"#);
let result = eval_spath(r#"$[?count(@.*) > 1]"#, &values).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @r#"[[1, 2, 3, 4, 5], {"e": 1, "f": 2}]"#);
}
#[test]
#[cfg(feature = "regex")]
fn test_regex_functions() {
let values = json! {[
{"timezone": "UTC", "offset": 0},
{"timezone": "CET", "offset": 1},
{"timezone": "PST", "offset": -8},
{"timezone": "JST", "offset": 9},
]};
let result = eval_spath(r#"$[?match(@.timezone, "...")].offset"#, &values).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @"[0, 1, -8, 9]");
let result = eval_spath(r#"$[?match(@.timezone, "..")].offset"#, &values).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @"[]");
let result = eval_spath(r#"$[?search(@.timezone, "ST")].offset"#, &values).unwrap();
let result = result.all();
assert_compact_json_snapshot!(result, @"[-8, 9]");
}