use std::cmp::Ordering;
use serde_json::{json, Value};
use serde_json_path::{JsonPath, JsonPathExt};
use serde_json_path_core::spec::functions::{NodesType, ValueType};
#[cfg(feature = "trace")]
use test_log::test;
#[test]
fn test_length() {
let value = json!([
"a short string",
"a slightly longer string",
"a string that is even longer!",
]);
let query = JsonPath::parse("$[?length(@) > 14]").unwrap();
let nodes = query.query(&value);
println!("{nodes:#?}");
assert_eq!(nodes.len(), 2);
}
#[test]
fn test_length_invalid_args() {
let error = JsonPath::parse("$[? length(@.foo.*)]").unwrap_err();
println!("{error:#?}");
}
#[test]
fn test_length_incorrect_use() {
let error = JsonPath::parse("$[?length(@.author)]").unwrap_err();
assert!(
error
.to_string()
.contains("in long-hand segment, function with incorrect return type used"),
"error did not contain the expected message"
);
}
#[test]
fn test_count() {
let value = json!([
{"foo": [1]},
{"foo": [1, 2]},
]);
let path = JsonPath::parse("$[?count(@.foo.*) > 1]").unwrap();
let q = path.query(&value);
assert_eq!(1, q.len());
}
#[test]
fn test_count_singular_query() {
let value = json!([
{"foo": "bar"},
{"foo": "baz"},
]);
let path = JsonPath::parse("$[? count(@.foo) == 1]").unwrap();
let q = path.query(&value);
println!("{q:#?}");
assert_eq!(q.len(), 2);
}
fn simpsons_characters() -> Value {
json!([
{ "name": "Homer Simpson" },
{ "name": "Marge Simpson" },
{ "name": "Bart Simpson" },
{ "name": "Lisa Simpson" },
{ "name": "Maggie Simpson" },
{ "name": "Ned Flanders" },
{ "name": "Rod Flanders" },
{ "name": "Todd Flanders" },
])
}
#[test]
fn test_match() {
let value = simpsons_characters();
let path = JsonPath::parse("$[? match(@.name, 'M[A-Za-z ]*Simpson')].name").unwrap();
let nodes = path.query(&value);
println!("{nodes:#?}");
assert_eq!(2, nodes.len());
assert_eq!("Marge Simpson", nodes.first().unwrap().as_str().unwrap(),);
}
#[test]
fn test_search() {
let value = simpsons_characters();
let path = JsonPath::parse("$[? search(@.name, 'Flanders')]").unwrap();
let nodes = path.query(&value);
println!("{nodes:#?}");
assert_eq!(3, nodes.len());
}
fn get_some_books() -> Value {
json!([
{
"books": [
{
"author": "Alexandre Dumas",
"title": "The Three Musketeers",
"price": 14.99
},
{
"author": "Leo Tolstoy",
"title": "War and Peace",
"price": 19.99
}
]
},
{
"books": [
{
"author": "Charles Dickens",
"title": "Great Expectations",
"price": 13.99
},
{
"author": "Fyodor Dostoevsky",
"title": "The Brothers Karamazov",
"price": 12.99
}
]
}
])
}
#[serde_json_path::function]
fn first(nodes: NodesType) -> ValueType {
match nodes.first() {
Some(v) => ValueType::Node(v),
None => ValueType::Nothing,
}
}
#[serde_json_path::function]
fn sort<'a>(nodes: NodesType<'a>, on: ValueType<'a>) -> NodesType<'a> {
if let Some(Ok(path)) = on.as_value().and_then(Value::as_str).map(JsonPath::parse) {
let mut nl = nodes.all();
nl.sort_by(|a, b| {
if let (Some(a), Some(b)) = (
a.json_path(&path).exactly_one().ok(),
b.json_path(&path).exactly_one().ok(),
) {
match (a, b) {
(Value::Bool(b1), Value::Bool(b2)) => b1.cmp(b2),
(Value::Number(n1), Value::Number(n2)) => {
if let (Some(f1), Some(f2)) = (n1.as_f64(), n2.as_f64()) {
f1.partial_cmp(&f2).unwrap_or(Ordering::Equal)
} else if let (Some(u1), Some(u2)) = (n1.as_u64(), n2.as_u64()) {
u1.cmp(&u2)
} else if let (Some(i1), Some(i2)) = (n1.as_i64(), n2.as_i64()) {
i1.cmp(&i2)
} else {
Ordering::Equal
}
}
(Value::String(s1), Value::String(s2)) => s1.cmp(s2),
_ => Ordering::Equal,
}
} else {
Ordering::Equal
}
});
nl.into()
} else {
nodes
}
}
#[serde_json_path::function]
fn get<'a>(node: ValueType<'a>, path: ValueType<'a>) -> ValueType<'a> {
if let Some(Ok(path)) = path.as_value().and_then(Value::as_str).map(JsonPath::parse) {
match node {
ValueType::Node(v) => v
.json_path(&path)
.exactly_one()
.map(ValueType::Node)
.unwrap_or_default(),
_ => ValueType::Nothing,
}
} else {
ValueType::Nothing
}
}
#[test]
fn custom_first_function() {
let value = get_some_books();
let path = JsonPath::parse("$[? first(@.books.*.author) == 'Alexandre Dumas']").unwrap();
let node = path.query(&value).exactly_one().unwrap();
println!("{node:#?}");
assert_eq!(
"War and Peace",
node.pointer("/books/1/title").unwrap().as_str().unwrap(),
);
}
#[test]
fn function_as_argument() {
let value = get_some_books();
let path = JsonPath::parse("$[? length(first(@.books.*.title)) == 18 ]").unwrap();
let node = path.query(&value).exactly_one().unwrap();
println!("{node:#?}");
assert_eq!(
"The Brothers Karamazov",
node.pointer("/books/1/title").unwrap().as_str().unwrap(),
)
}
#[test]
fn combine_custom_functions() {
let value = get_some_books();
let path = JsonPath::parse(
"$[? get(first(sort(@.books.*, '$.price')), '$.title') == 'The Brothers Karamazov']",
)
.unwrap();
let node = path.query(&value).exactly_one().unwrap();
println!("{node:#?}");
assert_eq!(
"The Brothers Karamazov",
node.pointer("/books/1/title").unwrap().as_str().unwrap(),
);
}