mod accessor;
mod construction;
mod primitives;
use nom::{
Parser,
branch::alt,
character::complete::{char, multispace0},
combinator::all_consuming,
multi::many0,
sequence::{delimited, preceded},
};
use simd_json::OwnedValue as Value;
use simd_json::StaticNode;
use super::ast::{Filter, FilterKind, FunctionCall, Span};
use super::builtins::Builtin;
use crate::error::ParseError;
use primitives::{Input, make_span};
pub fn parse(input: &str) -> Result<Filter, ParseError> {
let input = Input::new(input);
match all_consuming(parse_filter).parse(input) {
Ok((_, filter)) => Ok(filter),
Err(e) => {
let (position, message) = match &e {
nom::Err::Error(e) | nom::Err::Failure(e) => {
let pos = e.input.location_offset();
let fragment = *e.input.fragment();
let msg = if fragment.is_empty() {
"unexpected end of input".to_string()
} else {
format!("unexpected '{}'", fragment.chars().next().unwrap_or('?'))
};
(pos, msg)
}
nom::Err::Incomplete(_) => {
(input.location_offset(), "incomplete input".to_string())
}
};
Err(ParseError { message, position })
}
}
}
fn parse_filter(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
delimited(multispace0, parse_pipe_chain, multispace0).parse(input)
}
pub(crate) fn parse_pipe_chain(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
let (input, first) = parse_alternative_expr(input)?;
let (input, rest) = many0(preceded(
delimited(multispace0, char('|'), multispace0),
parse_alternative_expr,
))
.parse(input)?;
let filter = rest.into_iter().fold(first, |acc, f| {
let span = Span::new(acc.span.start, f.span.end);
Filter::new(FilterKind::Pipe(Box::new(acc), Box::new(f)), span)
});
Ok((input, filter))
}
fn parse_alternative_expr(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
use nom::bytes::complete::tag;
let (input, first) = parse_or_expr(input)?;
let (input, rest) = many0(preceded(
delimited(multispace0, tag("//"), multispace0),
parse_or_expr,
))
.parse(input)?;
let filter = rest.into_iter().fold(first, |acc, f| {
let span = Span::new(acc.span.start, f.span.end);
Filter::new(FilterKind::Alternative(Box::new(acc), Box::new(f)), span)
});
Ok((input, filter))
}
fn parse_or_expr(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
use super::ast::BoolOp;
use nom::bytes::complete::tag;
let (input, first) = parse_and_expr(input)?;
let (input, rest) = many0(preceded(
delimited(multispace0, tag("or"), multispace0),
parse_and_expr,
))
.parse(input)?;
let filter = rest.into_iter().fold(first, |acc, f| {
let span = Span::new(acc.span.start, f.span.end);
Filter::new(
FilterKind::BoolOp(Box::new(acc), BoolOp::Or, Box::new(f)),
span,
)
});
Ok((input, filter))
}
fn parse_and_expr(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
use super::ast::BoolOp;
use nom::bytes::complete::tag;
let (input, first) = parse_comparison_expr(input)?;
let (input, rest) = many0(preceded(
delimited(multispace0, tag("and"), multispace0),
parse_comparison_expr,
))
.parse(input)?;
let filter = rest.into_iter().fold(first, |acc, f| {
let span = Span::new(acc.span.start, f.span.end);
Filter::new(
FilterKind::BoolOp(Box::new(acc), BoolOp::And, Box::new(f)),
span,
)
});
Ok((input, filter))
}
fn parse_comparison_expr(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
let (input, left) = parse_additive_expr(input)?;
let (input, _) = multispace0(input)?;
if let Ok((input, op)) = primitives::parse_compare_op(input) {
let (input, _) = multispace0(input)?;
let (input, right) = parse_additive_expr(input)?;
let span = Span::new(left.span.start, right.span.end);
Ok((
input,
Filter::new(
FilterKind::Compare(Box::new(left), op, Box::new(right)),
span,
),
))
} else {
Ok((input, left))
}
}
fn parse_additive_expr(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
use super::ast::ArithOp;
use nom::bytes::complete::tag;
let (input, first) = parse_multiplicative_expr(input)?;
let mut result = first;
let mut remaining = input;
loop {
let (input, _) = multispace0(remaining)?;
let op_result: nom::IResult<Input<'_>, ArithOp> = alt((
tag("+").map(|_| ArithOp::Add),
tag("-").map(|_| ArithOp::Sub),
))
.parse(input);
match op_result {
Ok((input, op)) => {
let (input, _) = multispace0(input)?;
let (input, right) = parse_multiplicative_expr(input)?;
let span = Span::new(result.span.start, right.span.end);
result = Filter::new(
FilterKind::Arith(Box::new(result), op, Box::new(right)),
span,
);
remaining = input;
}
Err(_) => break,
}
}
Ok((remaining, result))
}
fn parse_multiplicative_expr(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
use super::ast::ArithOp;
use nom::bytes::complete::tag;
use nom::combinator::not;
let (input, first) = parse_filter_term(input)?;
let mut result = first;
let mut remaining = input;
loop {
let (input, _) = multispace0(remaining)?;
let op_result: nom::IResult<Input<'_>, ArithOp> = alt((
tag("*").map(|_| ArithOp::Mul),
(tag("/"), not(tag("/"))).map(|_| ArithOp::Div),
))
.parse(input);
match op_result {
Ok((input, op)) => {
let (input, _) = multispace0(input)?;
let (input, right) = parse_filter_term(input)?;
let span = Span::new(result.span.start, right.span.end);
result = Filter::new(
FilterKind::Arith(Box::new(result), op, Box::new(right)),
span,
);
remaining = input;
}
Err(_) => break,
}
}
Ok((remaining, result))
}
fn parse_filter_term(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
alt((
construction::parse_array_construction,
construction::parse_object_construction,
parse_if_then_else,
parse_function_call,
parse_paren_expr,
parse_literal,
accessor::parse_accessor_chain,
parse_format_function,
parse_builtin,
))
.parse(input)
}
fn parse_paren_expr(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
let (input, _) = char('(').parse(input)?;
let (input, _) = multispace0(input)?;
let (input, inner) = parse_pipe_chain(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char(')').parse(input)?;
Ok((input, inner))
}
fn parse_if_then_else(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
use nom::bytes::complete::tag;
let start = input;
let (rest, _) = tag("if").parse(input)?;
if rest
.fragment()
.chars()
.next()
.is_some_and(|c| c.is_alphanumeric() || c == '_')
{
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Tag,
)));
}
let (rest, _) = multispace0(rest)?;
let (rest, condition) = parse_pipe_chain(rest)?;
let (rest, _) = multispace0(rest)?;
let (rest, _) = tag("then").parse(rest)?;
let (rest, _) = multispace0(rest)?;
let (rest, then_branch) = parse_pipe_chain(rest)?;
let (rest, _) = multispace0(rest)?;
let (rest, _) = tag("else").parse(rest)?;
let (rest, _) = multispace0(rest)?;
let (rest, else_branch) = parse_pipe_chain(rest)?;
let (rest, _) = multispace0(rest)?;
let (end, _) = tag("end").parse(rest)?;
let span = make_span(&start, &end);
Ok((
end,
Filter::new(
FilterKind::IfThenElse {
condition: Box::new(condition),
then_branch: Box::new(then_branch),
else_branch: Box::new(else_branch),
},
span,
),
))
}
fn parse_literal(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
alt((
parse_null_literal,
parse_bool_literal,
parse_string_literal,
parse_number_literal,
))
.parse(input)
}
fn parse_null_literal(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
use nom::bytes::complete::tag;
let start = input;
let (rest, _) = tag("null").parse(input)?;
if rest
.fragment()
.chars()
.next()
.is_some_and(|c| c.is_alphanumeric() || c == '_')
{
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Tag,
)));
}
let span = make_span(&start, &rest);
Ok((
rest,
Filter::new(FilterKind::Literal(Value::Static(StaticNode::Null)), span),
))
}
fn parse_bool_literal(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
use nom::bytes::complete::tag;
let start = input;
let (rest, value) = alt((
tag("true").map(|_| Value::Static(StaticNode::Bool(true))),
tag("false").map(|_| Value::Static(StaticNode::Bool(false))),
))
.parse(input)?;
if rest
.fragment()
.chars()
.next()
.is_some_and(|c| c.is_alphanumeric() || c == '_')
{
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Tag,
)));
}
let span = make_span(&start, &rest);
Ok((rest, Filter::new(FilterKind::Literal(value), span)))
}
fn parse_string_literal(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
let start = input;
let (end, s) = primitives::parse_string_literal(input)?;
let span = make_span(&start, &end);
Ok((
end,
Filter::new(FilterKind::Literal(Value::String(s)), span),
))
}
fn parse_number_literal(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
let start = input;
let (end, n) = primitives::parse_number(input)?;
let span = make_span(&start, &end);
Ok((
end,
Filter::new(FilterKind::Literal(Value::Static(n)), span),
))
}
fn parse_function_call(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
let start = input;
let (rest, name) = primitives::identifier(input)?;
if !matches!(
name.as_str(),
"map"
| "select"
| "sort_by"
| "group_by"
| "unique_by"
| "min_by"
| "max_by"
| "has"
| "split"
| "join"
| "startswith"
| "endswith"
| "contains"
| "with_entries"
) {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Tag,
)));
}
let (rest, _) = multispace0(rest)?;
let (rest, _) = char('(').parse(rest)?;
let (rest, _) = multispace0(rest)?;
if matches!(
name.as_str(),
"has" | "split" | "join" | "startswith" | "endswith"
) {
let (rest, arg) = primitives::parse_string_literal(rest)?;
let (rest, _) = multispace0(rest)?;
let (end, _) = char(')').parse(rest)?;
let func = match name.as_str() {
"has" => FunctionCall::Has(arg),
"split" => FunctionCall::Split(arg),
"join" => FunctionCall::Join(arg),
"startswith" => FunctionCall::StartsWith(arg),
"endswith" => FunctionCall::EndsWith(arg),
_ => unreachable!(),
};
let span = make_span(&start, &end);
return Ok((end, Filter::new(FilterKind::Function(func), span)));
}
let (rest, inner) = parse_pipe_chain(rest)?;
let (rest, _) = multispace0(rest)?;
let (end, _) = char(')').parse(rest)?;
let func = match name.as_str() {
"map" => FunctionCall::Map(Box::new(inner)),
"select" => FunctionCall::Select(Box::new(inner)),
"sort_by" => FunctionCall::SortBy(Box::new(inner)),
"group_by" => FunctionCall::GroupBy(Box::new(inner)),
"unique_by" => FunctionCall::UniqueBy(Box::new(inner)),
"min_by" => FunctionCall::MinBy(Box::new(inner)),
"max_by" => FunctionCall::MaxBy(Box::new(inner)),
"contains" => FunctionCall::Contains(Box::new(inner)),
"with_entries" => FunctionCall::WithEntries(Box::new(inner)),
_ => unreachable!(),
};
let span = make_span(&start, &end);
Ok((end, Filter::new(FilterKind::Function(func), span)))
}
fn parse_builtin(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
let start = input;
let (end, name) = primitives::identifier(input)?;
match Builtin::from_name(&name) {
Some(b) => {
let span = make_span(&start, &end);
Ok((end, Filter::new(FilterKind::Builtin(b), span)))
}
None => Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Tag,
))),
}
}
fn parse_format_function(input: Input<'_>) -> nom::IResult<Input<'_>, Filter> {
use nom::bytes::complete::tag;
let start = input;
let (rest, builtin) = alt((
tag("@csv").map(|_| Builtin::Csv),
tag("@tsv").map(|_| Builtin::Tsv),
))
.parse(input)?;
let span = make_span(&start, &rest);
Ok((rest, Filter::new(FilterKind::Builtin(builtin), span)))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::filter::ast::ObjectKey;
fn parse_kind(input: &str) -> Result<FilterKind, ParseError> {
parse(input).map(|f| f.kind)
}
#[test]
fn test_identity() {
assert_eq!(parse_kind("."), Ok(FilterKind::Identity));
}
#[test]
fn test_identity_with_whitespace() {
assert_eq!(parse_kind(" . "), Ok(FilterKind::Identity));
}
#[test]
fn test_field() {
assert_eq!(parse_kind(".foo"), Ok(FilterKind::Field("foo".to_string())));
assert_eq!(
parse_kind(".name"),
Ok(FilterKind::Field("name".to_string()))
);
}
#[test]
fn test_field_with_whitespace() {
assert_eq!(
parse_kind(" .foo "),
Ok(FilterKind::Field("foo".to_string()))
);
}
#[test]
fn test_field_chain() {
let result = parse(".foo.bar").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("foo".to_string()));
assert_eq!(right.kind, FilterKind::Field("bar".to_string()));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_field_chain_triple() {
let result = parse(".foo.bar.baz").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(right.kind, FilterKind::Field("baz".to_string()));
match left.kind {
FilterKind::Pipe(ll, lr) => {
assert_eq!(ll.kind, FilterKind::Field("foo".to_string()));
assert_eq!(lr.kind, FilterKind::Field("bar".to_string()));
}
_ => panic!("Expected nested Pipe"),
}
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_underscore_field() {
assert_eq!(
parse_kind("._private"),
Ok(FilterKind::Field("_private".to_string()))
);
}
#[test]
fn test_field_with_numbers() {
assert_eq!(
parse_kind(".field123"),
Ok(FilterKind::Field("field123".to_string()))
);
}
#[test]
fn test_unicode_field() {
assert_eq!(
parse_kind(".名前"),
Ok(FilterKind::Field("名前".to_string()))
);
}
#[test]
fn test_keyword_as_field() {
assert_eq!(
parse_kind(".null"),
Ok(FilterKind::Field("null".to_string()))
);
assert_eq!(
parse_kind(".true"),
Ok(FilterKind::Field("true".to_string()))
);
assert_eq!(
parse_kind(".false"),
Ok(FilterKind::Field("false".to_string()))
);
}
#[test]
fn test_index() {
assert_eq!(parse_kind(".[0]"), Ok(FilterKind::Index(0)));
assert_eq!(parse_kind(".[1]"), Ok(FilterKind::Index(1)));
assert_eq!(parse_kind(".[42]"), Ok(FilterKind::Index(42)));
}
#[test]
fn test_negative_index() {
assert_eq!(parse_kind(".[-1]"), Ok(FilterKind::Index(-1)));
assert_eq!(parse_kind(".[-10]"), Ok(FilterKind::Index(-10)));
}
#[test]
fn test_index_with_whitespace() {
assert_eq!(parse_kind(".[ 0 ]"), Ok(FilterKind::Index(0)));
assert_eq!(parse_kind(".[ -1 ]"), Ok(FilterKind::Index(-1)));
}
#[test]
fn test_iterate() {
assert_eq!(parse_kind(".[]"), Ok(FilterKind::Iterate));
}
#[test]
fn test_iterate_with_whitespace() {
assert_eq!(parse_kind(".[ ]"), Ok(FilterKind::Iterate));
}
#[test]
fn test_quoted_field() {
assert_eq!(
parse_kind(".[\"foo\"]"),
Ok(FilterKind::Field("foo".to_string()))
);
}
#[test]
fn test_quoted_field_special_chars() {
assert_eq!(
parse_kind(".[\"foo-bar\"]"),
Ok(FilterKind::Field("foo-bar".to_string()))
);
assert_eq!(
parse_kind(".[\"with space\"]"),
Ok(FilterKind::Field("with space".to_string()))
);
assert_eq!(
parse_kind(".[\"a.b\"]"),
Ok(FilterKind::Field("a.b".to_string()))
);
}
#[test]
fn test_quoted_field_empty() {
assert_eq!(parse_kind(".[\"\"]"), Ok(FilterKind::Field("".to_string())));
}
#[test]
fn test_quoted_field_unicode() {
assert_eq!(
parse_kind(".[\"日本語\"]"),
Ok(FilterKind::Field("日本語".to_string()))
);
}
#[test]
fn test_quoted_field_with_whitespace() {
assert_eq!(
parse_kind(".[ \"foo\" ]"),
Ok(FilterKind::Field("foo".to_string()))
);
}
#[test]
fn test_quoted_field_escapes() {
assert_eq!(
parse_kind(".[\"a\\nb\"]"),
Ok(FilterKind::Field("a\nb".to_string()))
);
assert_eq!(
parse_kind(".[\"a\\tb\"]"),
Ok(FilterKind::Field("a\tb".to_string()))
);
assert_eq!(
parse_kind(".[\"a\\\"b\"]"),
Ok(FilterKind::Field("a\"b".to_string()))
);
assert_eq!(
parse_kind(".[\"a\\\\b\"]"),
Ok(FilterKind::Field("a\\b".to_string()))
);
}
#[test]
fn test_quoted_field_unicode_escape() {
assert_eq!(
parse_kind(".[\"\\u0041\"]"),
Ok(FilterKind::Field("A".to_string()))
);
}
#[test]
fn test_chain_field_index() {
let result = parse(".foo[0]").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("foo".to_string()));
assert_eq!(right.kind, FilterKind::Index(0));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_chain_index_field() {
let result = parse(".[0].foo").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Index(0));
assert_eq!(right.kind, FilterKind::Field("foo".to_string()));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_chain_index_index() {
let result = parse(".[0][1]").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Index(0));
assert_eq!(right.kind, FilterKind::Index(1));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_chain_iterate_index() {
let result = parse(".[][0]").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Iterate);
assert_eq!(right.kind, FilterKind::Index(0));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_chain_quoted_quoted() {
let result = parse(".[\"a\"][\"b\"]").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("a".to_string()));
assert_eq!(right.kind, FilterKind::Field("b".to_string()));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_chain_mixed() {
let result = parse(".items[0].id").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(right.kind, FilterKind::Field("id".to_string()));
match left.kind {
FilterKind::Pipe(ll, lr) => {
assert_eq!(ll.kind, FilterKind::Field("items".to_string()));
assert_eq!(lr.kind, FilterKind::Index(0));
}
_ => panic!("Expected nested Pipe"),
}
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_chain_iterate_field() {
let result = parse(".items[].id").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(right.kind, FilterKind::Field("id".to_string()));
match left.kind {
FilterKind::Pipe(ll, lr) => {
assert_eq!(ll.kind, FilterKind::Field("items".to_string()));
assert_eq!(lr.kind, FilterKind::Iterate);
}
_ => panic!("Expected nested Pipe"),
}
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_chain_mixed_dot_bracket() {
let result = parse(".foo[\"bar\"].baz").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(right.kind, FilterKind::Field("baz".to_string()));
match left.kind {
FilterKind::Pipe(ll, lr) => {
assert_eq!(ll.kind, FilterKind::Field("foo".to_string()));
assert_eq!(lr.kind, FilterKind::Field("bar".to_string()));
}
_ => panic!("Expected nested Pipe"),
}
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_trailing_dot_error() {
assert!(parse(".foo.").is_err());
}
#[test]
fn test_digit_start_error() {
assert!(parse(".123").is_err());
}
#[test]
fn test_empty() {
assert!(parse("").is_err());
}
#[test]
fn test_invalid() {
assert!(parse("..").is_err());
assert!(parse("foo").is_err());
}
#[test]
fn test_unclosed_bracket() {
assert!(parse(".[").is_err());
assert!(parse(".[0").is_err());
}
#[test]
fn test_unclosed_string() {
assert!(parse(".[\"unclosed").is_err());
}
#[test]
fn test_single_quotes_error() {
assert!(parse(".['field']").is_err());
}
#[test]
fn test_float_error() {
assert!(parse(".[0.5]").is_err());
}
#[test]
fn test_integer_overflow() {
assert!(parse(".[99999999999999999999]").is_err());
}
#[test]
fn test_pipe_simple() {
let result = parse(".foo | .bar").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("foo".to_string()));
assert_eq!(right.kind, FilterKind::Field("bar".to_string()));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_pipe_with_whitespace() {
let result1 = parse(".foo|.bar").unwrap();
let result2 = parse(".foo | .bar").unwrap();
match (&result1.kind, &result2.kind) {
(FilterKind::Pipe(l1, r1), FilterKind::Pipe(l2, r2)) => {
assert_eq!(l1.kind, FilterKind::Field("foo".to_string()));
assert_eq!(r1.kind, FilterKind::Field("bar".to_string()));
assert_eq!(l2.kind, FilterKind::Field("foo".to_string()));
assert_eq!(r2.kind, FilterKind::Field("bar".to_string()));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_pipe_triple() {
let result = parse(".a | .b | .c").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(right.kind, FilterKind::Field("c".to_string()));
match left.kind {
FilterKind::Pipe(ll, lr) => {
assert_eq!(ll.kind, FilterKind::Field("a".to_string()));
assert_eq!(lr.kind, FilterKind::Field("b".to_string()));
}
_ => panic!("Expected nested Pipe"),
}
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_pipe_with_iterate() {
let result = parse(".users | .[]").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("users".to_string()));
assert_eq!(right.kind, FilterKind::Iterate);
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_pipe_with_index() {
let result = parse(".items | .[0]").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("items".to_string()));
assert_eq!(right.kind, FilterKind::Index(0));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_pipe_mixed_implicit_explicit() {
let result = parse(".users[].name | .foo").unwrap();
match result.kind {
FilterKind::Pipe(_left, right) => {
assert_eq!(right.kind, FilterKind::Field("foo".to_string()));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_pipe_identity() {
let result = parse(". | .foo").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Identity);
assert_eq!(right.kind, FilterKind::Field("foo".to_string()));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_pipe_trailing_error() {
assert!(parse(".foo |").is_err());
}
#[test]
fn test_pipe_leading_error() {
assert!(parse("| .foo").is_err());
}
#[test]
fn test_pipe_double_error() {
assert!(parse(".foo || .bar").is_err());
}
#[test]
fn test_pipe_empty_segment_error() {
assert!(parse(".foo | | .bar").is_err());
}
#[test]
fn test_builtin_standalone() {
assert_eq!(
parse_kind("length"),
Ok(FilterKind::Builtin(Builtin::Length))
);
assert_eq!(parse_kind("keys"), Ok(FilterKind::Builtin(Builtin::Keys)));
assert_eq!(parse_kind("type"), Ok(FilterKind::Builtin(Builtin::Type)));
}
#[test]
fn test_builtin_with_whitespace() {
assert_eq!(
parse_kind(" length "),
Ok(FilterKind::Builtin(Builtin::Length))
);
}
#[test]
fn test_builtin_after_pipe() {
let result = parse(".foo | length").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("foo".to_string()));
assert_eq!(right.kind, FilterKind::Builtin(Builtin::Length));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_builtin_before_pipe() {
let result = parse("length | .foo").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Builtin(Builtin::Length));
assert_eq!(right.kind, FilterKind::Field("foo".to_string()));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_builtin_chain() {
let result = parse(".items | length").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("items".to_string()));
assert_eq!(right.kind, FilterKind::Builtin(Builtin::Length));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_iterate_then_builtin() {
let result = parse(".[] | length").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Iterate);
assert_eq!(right.kind, FilterKind::Builtin(Builtin::Length));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_builtin_then_iterate() {
let result = parse("keys | .[]").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Builtin(Builtin::Keys));
assert_eq!(right.kind, FilterKind::Iterate);
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_multiple_builtins() {
let result = parse("sort | reverse").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Builtin(Builtin::Sort));
assert_eq!(right.kind, FilterKind::Builtin(Builtin::Reverse));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_all_builtins_parse() {
assert_eq!(
parse_kind("length"),
Ok(FilterKind::Builtin(Builtin::Length))
);
assert_eq!(parse_kind("keys"), Ok(FilterKind::Builtin(Builtin::Keys)));
assert_eq!(
parse_kind("values"),
Ok(FilterKind::Builtin(Builtin::Values))
);
assert_eq!(parse_kind("type"), Ok(FilterKind::Builtin(Builtin::Type)));
assert_eq!(parse_kind("first"), Ok(FilterKind::Builtin(Builtin::First)));
assert_eq!(parse_kind("last"), Ok(FilterKind::Builtin(Builtin::Last)));
assert_eq!(
parse_kind("reverse"),
Ok(FilterKind::Builtin(Builtin::Reverse))
);
assert_eq!(parse_kind("sort"), Ok(FilterKind::Builtin(Builtin::Sort)));
assert_eq!(parse_kind("min"), Ok(FilterKind::Builtin(Builtin::Min)));
assert_eq!(parse_kind("max"), Ok(FilterKind::Builtin(Builtin::Max)));
assert_eq!(
parse_kind("unique"),
Ok(FilterKind::Builtin(Builtin::Unique))
);
assert_eq!(
parse_kind("flatten"),
Ok(FilterKind::Builtin(Builtin::Flatten))
);
assert_eq!(parse_kind("add"), Ok(FilterKind::Builtin(Builtin::Add)));
assert_eq!(parse_kind("empty"), Ok(FilterKind::Builtin(Builtin::Empty)));
assert_eq!(parse_kind("not"), Ok(FilterKind::Builtin(Builtin::Not)));
}
#[test]
fn test_unknown_identifier_error() {
assert!(parse("notabuiltin").is_err());
}
#[test]
fn test_builtin_as_field_name() {
assert_eq!(
parse_kind(".length"),
Ok(FilterKind::Field("length".to_string()))
);
}
#[test]
fn test_array_construction_simple() {
let result = parse("[.foo, .bar]").unwrap();
match result.kind {
FilterKind::Array(elems) => {
assert_eq!(elems.len(), 2);
assert_eq!(elems[0].kind, FilterKind::Field("foo".to_string()));
assert_eq!(elems[1].kind, FilterKind::Field("bar".to_string()));
}
_ => panic!("Expected Array"),
}
}
#[test]
fn test_array_construction_empty() {
assert_eq!(parse_kind("[]"), Ok(FilterKind::Array(vec![])));
}
#[test]
fn test_array_construction_single() {
let result = parse("[.foo]").unwrap();
match result.kind {
FilterKind::Array(elems) => {
assert_eq!(elems.len(), 1);
assert_eq!(elems[0].kind, FilterKind::Field("foo".to_string()));
}
_ => panic!("Expected Array"),
}
}
#[test]
fn test_array_construction_with_pipe() {
let result = parse("[.foo | length]").unwrap();
match result.kind {
FilterKind::Array(elems) => {
assert_eq!(elems.len(), 1);
match &elems[0].kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("foo".to_string()));
assert_eq!(right.kind, FilterKind::Builtin(Builtin::Length));
}
_ => panic!("Expected Pipe"),
}
}
_ => panic!("Expected Array"),
}
}
#[test]
fn test_array_construction_with_iterate() {
let result = parse("[.[]]").unwrap();
match result.kind {
FilterKind::Array(elems) => {
assert_eq!(elems.len(), 1);
assert_eq!(elems[0].kind, FilterKind::Iterate);
}
_ => panic!("Expected Array"),
}
}
#[test]
fn test_array_construction_nested() {
let result = parse("[[.x], [.y]]").unwrap();
match result.kind {
FilterKind::Array(elems) => {
assert_eq!(elems.len(), 2);
}
_ => panic!("Expected Array"),
}
}
#[test]
fn test_array_construction_with_whitespace() {
let result = parse("[ .foo , .bar ]").unwrap();
match result.kind {
FilterKind::Array(elems) => {
assert_eq!(elems.len(), 2);
}
_ => panic!("Expected Array"),
}
}
#[test]
fn test_object_construction_simple() {
let result = parse("{name: .foo}").unwrap();
match result.kind {
FilterKind::Object(pairs) => {
assert_eq!(pairs.len(), 1);
assert_eq!(pairs[0].0, ObjectKey::Static("name".to_string()));
assert_eq!(pairs[0].1.kind, FilterKind::Field("foo".to_string()));
}
_ => panic!("Expected Object"),
}
}
#[test]
fn test_object_construction_empty() {
assert_eq!(parse_kind("{}"), Ok(FilterKind::Object(vec![])));
}
#[test]
fn test_object_construction_multiple() {
let result = parse("{name: .foo, age: .bar}").unwrap();
match result.kind {
FilterKind::Object(pairs) => {
assert_eq!(pairs.len(), 2);
}
_ => panic!("Expected Object"),
}
}
#[test]
fn test_object_construction_quoted_key() {
let result = parse("{\"my-key\": .foo}").unwrap();
match result.kind {
FilterKind::Object(pairs) => {
assert_eq!(pairs[0].0, ObjectKey::Static("my-key".to_string()));
}
_ => panic!("Expected Object"),
}
}
#[test]
fn test_object_construction_dynamic_key() {
let result = parse("{(.key): .value}").unwrap();
match result.kind {
FilterKind::Object(pairs) => match &pairs[0].0 {
ObjectKey::Dynamic(f) => {
assert_eq!(f.kind, FilterKind::Field("key".to_string()));
}
_ => panic!("Expected Dynamic key"),
},
_ => panic!("Expected Object"),
}
}
#[test]
fn test_object_construction_with_pipe() {
let result = parse("{count: .items | length}").unwrap();
match result.kind {
FilterKind::Object(pairs) => match &pairs[0].1.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("items".to_string()));
assert_eq!(right.kind, FilterKind::Builtin(Builtin::Length));
}
_ => panic!("Expected Pipe"),
},
_ => panic!("Expected Object"),
}
}
#[test]
fn test_object_construction_with_whitespace() {
let result = parse("{ name : .foo , age : .bar }").unwrap();
match result.kind {
FilterKind::Object(pairs) => {
assert_eq!(pairs.len(), 2);
}
_ => panic!("Expected Object"),
}
}
#[test]
fn test_nested_object_in_array() {
let result = parse("[{a: .x}]").unwrap();
match result.kind {
FilterKind::Array(elems) => match &elems[0].kind {
FilterKind::Object(pairs) => {
assert_eq!(pairs[0].0, ObjectKey::Static("a".to_string()));
}
_ => panic!("Expected Object"),
},
_ => panic!("Expected Array"),
}
}
#[test]
fn test_array_in_object() {
let result = parse("{point: [.x, .y]}").unwrap();
match result.kind {
FilterKind::Object(pairs) => match &pairs[0].1.kind {
FilterKind::Array(elems) => {
assert_eq!(elems.len(), 2);
}
_ => panic!("Expected Array"),
},
_ => panic!("Expected Object"),
}
}
#[test]
fn test_optional_field() {
let result = parse(".foo?").unwrap();
match result.kind {
FilterKind::Optional(inner) => {
assert_eq!(inner.kind, FilterKind::Field("foo".to_string()));
}
_ => panic!("Expected Optional"),
}
}
#[test]
fn test_optional_index() {
let result = parse(".[0]?").unwrap();
match result.kind {
FilterKind::Optional(inner) => {
assert_eq!(inner.kind, FilterKind::Index(0));
}
_ => panic!("Expected Optional"),
}
}
#[test]
fn test_optional_iterate() {
let result = parse(".[]?").unwrap();
match result.kind {
FilterKind::Optional(inner) => {
assert_eq!(inner.kind, FilterKind::Iterate);
}
_ => panic!("Expected Optional"),
}
}
#[test]
fn test_optional_chained() {
let result = parse(".foo?.bar").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
match left.kind {
FilterKind::Optional(inner) => {
assert_eq!(inner.kind, FilterKind::Field("foo".to_string()));
}
_ => panic!("Expected Optional"),
}
assert_eq!(right.kind, FilterKind::Field("bar".to_string()));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_optional_both() {
let result = parse(".foo?.bar?").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
match &left.kind {
FilterKind::Optional(inner) => {
assert_eq!(inner.kind, FilterKind::Field("foo".to_string()));
}
_ => panic!("Expected Optional"),
}
match &right.kind {
FilterKind::Optional(inner) => {
assert_eq!(inner.kind, FilterKind::Field("bar".to_string()));
}
_ => panic!("Expected Optional"),
}
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_optional_quoted_field() {
let result = parse(".[\"foo\"]?").unwrap();
match result.kind {
FilterKind::Optional(inner) => {
assert_eq!(inner.kind, FilterKind::Field("foo".to_string()));
}
_ => panic!("Expected Optional"),
}
}
#[test]
fn test_slice_both() {
assert_eq!(
parse_kind(".[2:5]"),
Ok(FilterKind::Slice(Some(2), Some(5)))
);
}
#[test]
fn test_slice_start_only() {
assert_eq!(parse_kind(".[2:]"), Ok(FilterKind::Slice(Some(2), None)));
}
#[test]
fn test_slice_end_only() {
assert_eq!(parse_kind(".[:5]"), Ok(FilterKind::Slice(None, Some(5))));
}
#[test]
fn test_slice_neither() {
assert_eq!(parse_kind(".[:]"), Ok(FilterKind::Slice(None, None)));
}
#[test]
fn test_slice_negative_start() {
assert_eq!(parse_kind(".[-2:]"), Ok(FilterKind::Slice(Some(-2), None)));
}
#[test]
fn test_slice_negative_end() {
assert_eq!(parse_kind(".[:-1]"), Ok(FilterKind::Slice(None, Some(-1))));
}
#[test]
fn test_slice_both_negative() {
assert_eq!(
parse_kind(".[-3:-1]"),
Ok(FilterKind::Slice(Some(-3), Some(-1)))
);
}
#[test]
fn test_slice_with_whitespace() {
assert_eq!(
parse_kind(".[ 2 : 5 ]"),
Ok(FilterKind::Slice(Some(2), Some(5)))
);
}
#[test]
fn test_slice_chained() {
let result = parse(".[2:5][0]").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Slice(Some(2), Some(5)));
assert_eq!(right.kind, FilterKind::Index(0));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_map_basic() {
let result = parse("map(.foo)").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Map(inner)) => {
assert_eq!(inner.kind, FilterKind::Field("foo".to_string()));
}
_ => panic!("Expected Function Map"),
}
}
#[test]
fn test_map_with_pipe() {
let result = parse("map(.a | .b)").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Map(inner)) => match inner.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("a".to_string()));
assert_eq!(right.kind, FilterKind::Field("b".to_string()));
}
_ => panic!("Expected Pipe"),
},
_ => panic!("Expected Function Map"),
}
}
#[test]
fn test_map_identity() {
let result = parse("map(.)").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Map(inner)) => {
assert_eq!(inner.kind, FilterKind::Identity);
}
_ => panic!("Expected Function Map"),
}
}
#[test]
fn test_map_with_whitespace() {
let result = parse("map( .foo )").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Map(inner)) => {
assert_eq!(inner.kind, FilterKind::Field("foo".to_string()));
}
_ => panic!("Expected Function Map"),
}
}
#[test]
fn test_select_basic() {
let result = parse("select(.active)").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Select(inner)) => {
assert_eq!(inner.kind, FilterKind::Field("active".to_string()));
}
_ => panic!("Expected Function Select"),
}
}
#[test]
fn test_select_with_pipe() {
let result = parse("select(.a | .b)").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Select(inner)) => match inner.kind {
FilterKind::Pipe(_, _) => {}
_ => panic!("Expected Pipe"),
},
_ => panic!("Expected Function Select"),
}
}
#[test]
fn test_function_in_pipe_chain() {
let result = parse(".items | map(.name)").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("items".to_string()));
match right.kind {
FilterKind::Function(FunctionCall::Map(inner)) => {
assert_eq!(inner.kind, FilterKind::Field("name".to_string()));
}
_ => panic!("Expected Function Map"),
}
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_map_without_parens_fails() {
assert!(parse("map").is_err());
}
#[test]
fn test_select_without_parens_fails() {
assert!(parse("select").is_err());
}
#[test]
fn test_map_nested_in_array() {
let result = parse("[map(.x)]").unwrap();
match result.kind {
FilterKind::Array(elems) => match &elems[0].kind {
FilterKind::Function(FunctionCall::Map(inner)) => {
assert_eq!(inner.kind, FilterKind::Field("x".to_string()));
}
_ => panic!("Expected Function Map"),
},
_ => panic!("Expected Array"),
}
}
#[test]
fn test_sort_by_basic() {
let result = parse("sort_by(.age)").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::SortBy(inner)) => {
assert_eq!(inner.kind, FilterKind::Field("age".to_string()));
}
_ => panic!("Expected Function SortBy"),
}
}
#[test]
fn test_group_by_basic() {
let result = parse("group_by(.type)").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::GroupBy(inner)) => {
assert_eq!(inner.kind, FilterKind::Field("type".to_string()));
}
_ => panic!("Expected Function GroupBy"),
}
}
#[test]
fn test_unique_by_basic() {
let result = parse("unique_by(.id)").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::UniqueBy(inner)) => {
assert_eq!(inner.kind, FilterKind::Field("id".to_string()));
}
_ => panic!("Expected Function UniqueBy"),
}
}
#[test]
fn test_min_by_basic() {
let result = parse("min_by(.score)").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::MinBy(inner)) => {
assert_eq!(inner.kind, FilterKind::Field("score".to_string()));
}
_ => panic!("Expected Function MinBy"),
}
}
#[test]
fn test_max_by_basic() {
let result = parse("max_by(.score)").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::MaxBy(inner)) => {
assert_eq!(inner.kind, FilterKind::Field("score".to_string()));
}
_ => panic!("Expected Function MaxBy"),
}
}
#[test]
fn test_sort_by_in_pipe() {
let result = parse(".items | sort_by(.name)").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("items".to_string()));
match right.kind {
FilterKind::Function(FunctionCall::SortBy(inner)) => {
assert_eq!(inner.kind, FilterKind::Field("name".to_string()));
}
_ => panic!("Expected Function SortBy"),
}
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_if_then_else_basic() {
let result = parse("if .x then .a else .b end").unwrap();
match result.kind {
FilterKind::IfThenElse {
condition,
then_branch,
else_branch,
} => {
assert_eq!(condition.kind, FilterKind::Field("x".to_string()));
assert_eq!(then_branch.kind, FilterKind::Field("a".to_string()));
assert_eq!(else_branch.kind, FilterKind::Field("b".to_string()));
}
_ => panic!("Expected IfThenElse"),
}
}
#[test]
fn test_if_then_else_with_comparison() {
use super::super::ast::CompareOp;
let result = parse("if . > 0 then \"pos\" else \"neg\" end").unwrap();
match result.kind {
FilterKind::IfThenElse {
condition,
then_branch: _,
else_branch: _,
} => match condition.kind {
FilterKind::Compare(left, op, _right) => {
assert_eq!(left.kind, FilterKind::Identity);
assert_eq!(op, CompareOp::Gt);
}
_ => panic!("Expected Compare"),
},
_ => panic!("Expected IfThenElse"),
}
}
#[test]
fn test_if_then_else_with_whitespace() {
assert!(parse("if . then . else . end").is_ok());
assert!(parse("if . then . else . end").is_ok());
}
#[test]
fn test_alternative_basic() {
let result = parse(".x // \"default\"").unwrap();
match result.kind {
FilterKind::Alternative(left, _right) => {
assert_eq!(left.kind, FilterKind::Field("x".to_string()));
}
_ => panic!("Expected Alternative"),
}
}
#[test]
fn test_alternative_chain() {
let result = parse(".a // .b // .c").unwrap();
match result.kind {
FilterKind::Alternative(left, right) => {
assert_eq!(right.kind, FilterKind::Field("c".to_string()));
match left.kind {
FilterKind::Alternative(ll, lr) => {
assert_eq!(ll.kind, FilterKind::Field("a".to_string()));
assert_eq!(lr.kind, FilterKind::Field("b".to_string()));
}
_ => panic!("Expected nested Alternative"),
}
}
_ => panic!("Expected Alternative"),
}
}
#[test]
fn test_alternative_in_pipe() {
let result = parse(".x | . // 0").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Field("x".to_string()));
match right.kind {
FilterKind::Alternative(_, _) => {}
_ => panic!("Expected Alternative"),
}
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_has_basic() {
let result = parse("has(\"name\")").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Has(s)) => {
assert_eq!(s, "name");
}
_ => panic!("Expected Function Has"),
}
}
#[test]
fn test_has_in_select() {
let result = parse("select(has(\"x\"))").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Select(inner)) => match inner.kind {
FilterKind::Function(FunctionCall::Has(s)) => {
assert_eq!(s, "x");
}
_ => panic!("Expected Function Has"),
},
_ => panic!("Expected Function Select"),
}
}
#[test]
fn test_literal_number_int() {
let result = parse("5").unwrap();
match result.kind {
FilterKind::Literal(v) => {
assert_eq!(v, simd_json::json!(5));
}
_ => panic!("Expected Literal"),
}
}
#[test]
fn test_literal_number_negative() {
let result = parse("-42").unwrap();
match result.kind {
FilterKind::Literal(v) => {
assert_eq!(v, simd_json::json!(-42));
}
_ => panic!("Expected Literal"),
}
}
#[test]
fn test_literal_number_float() {
let result = parse("3.15").unwrap();
match result.kind {
FilterKind::Literal(v) => {
assert_eq!(v, simd_json::json!(3.15));
}
_ => panic!("Expected Literal"),
}
}
#[test]
fn test_literal_string() {
let result = parse("\"hello\"").unwrap();
match result.kind {
FilterKind::Literal(v) => {
assert_eq!(v, simd_json::json!("hello"));
}
_ => panic!("Expected Literal"),
}
}
#[test]
fn test_literal_true() {
let result = parse("true").unwrap();
match result.kind {
FilterKind::Literal(v) => {
assert_eq!(v, simd_json::json!(true));
}
_ => panic!("Expected Literal"),
}
}
#[test]
fn test_literal_false() {
let result = parse("false").unwrap();
match result.kind {
FilterKind::Literal(v) => {
assert_eq!(v, simd_json::json!(false));
}
_ => panic!("Expected Literal"),
}
}
#[test]
fn test_literal_null() {
let result = parse("null").unwrap();
match result.kind {
FilterKind::Literal(v) => {
assert_eq!(v, simd_json::json!(null));
}
_ => panic!("Expected Literal"),
}
}
#[test]
fn test_compare_eq() {
use super::super::ast::CompareOp;
let result = parse(". == 5").unwrap();
match result.kind {
FilterKind::Compare(left, op, _right) => {
assert_eq!(left.kind, FilterKind::Identity);
assert_eq!(op, CompareOp::Eq);
}
_ => panic!("Expected Compare"),
}
}
#[test]
fn test_compare_ne() {
use super::super::ast::CompareOp;
let result = parse(".x != \"foo\"").unwrap();
match result.kind {
FilterKind::Compare(left, op, _right) => {
assert_eq!(left.kind, FilterKind::Field("x".to_string()));
assert_eq!(op, CompareOp::Ne);
}
_ => panic!("Expected Compare"),
}
}
#[test]
fn test_compare_lt() {
use super::super::ast::CompareOp;
let result = parse(".age < 18").unwrap();
match result.kind {
FilterKind::Compare(left, op, _right) => {
assert_eq!(left.kind, FilterKind::Field("age".to_string()));
assert_eq!(op, CompareOp::Lt);
}
_ => panic!("Expected Compare"),
}
}
#[test]
fn test_compare_le() {
use super::super::ast::CompareOp;
let result = parse(". <= 100").unwrap();
match result.kind {
FilterKind::Compare(left, op, _right) => {
assert_eq!(left.kind, FilterKind::Identity);
assert_eq!(op, CompareOp::Le);
}
_ => panic!("Expected Compare"),
}
}
#[test]
fn test_compare_gt() {
use super::super::ast::CompareOp;
let result = parse(". > 0").unwrap();
match result.kind {
FilterKind::Compare(left, op, _right) => {
assert_eq!(left.kind, FilterKind::Identity);
assert_eq!(op, CompareOp::Gt);
}
_ => panic!("Expected Compare"),
}
}
#[test]
fn test_compare_ge() {
use super::super::ast::CompareOp;
let result = parse(". >= -5").unwrap();
match result.kind {
FilterKind::Compare(left, op, _right) => {
assert_eq!(left.kind, FilterKind::Identity);
assert_eq!(op, CompareOp::Ge);
}
_ => panic!("Expected Compare"),
}
}
#[test]
fn test_compare_in_pipe() {
use super::super::ast::CompareOp;
let result = parse(".[] | . > 2").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Iterate);
match right.kind {
FilterKind::Compare(l, op, _r) => {
assert_eq!(l.kind, FilterKind::Identity);
assert_eq!(op, CompareOp::Gt);
}
_ => panic!("Expected Compare"),
}
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_compare_field_to_field() {
use super::super::ast::CompareOp;
let result = parse(".a == .b").unwrap();
match result.kind {
FilterKind::Compare(left, op, right) => {
assert_eq!(left.kind, FilterKind::Field("a".to_string()));
assert_eq!(op, CompareOp::Eq);
assert_eq!(right.kind, FilterKind::Field("b".to_string()));
}
_ => panic!("Expected Compare"),
}
}
#[test]
fn test_split() {
let result = parse("split(\"/\")").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Split(s)) => {
assert_eq!(s, "/");
}
_ => panic!("Expected Function Split"),
}
}
#[test]
fn test_split_empty_delim() {
let result = parse("split(\"\")").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Split(s)) => {
assert_eq!(s, "");
}
_ => panic!("Expected Function Split"),
}
}
#[test]
fn test_join() {
let result = parse("join(\"-\")").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Join(s)) => {
assert_eq!(s, "-");
}
_ => panic!("Expected Function Join"),
}
}
#[test]
fn test_startswith() {
let result = parse("startswith(\"hello\")").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::StartsWith(s)) => {
assert_eq!(s, "hello");
}
_ => panic!("Expected Function StartsWith"),
}
}
#[test]
fn test_endswith() {
let result = parse("endswith(\".json\")").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::EndsWith(s)) => {
assert_eq!(s, ".json");
}
_ => panic!("Expected Function EndsWith"),
}
}
#[test]
fn test_contains_string() {
let result = parse("contains(\"foo\")").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Contains(inner)) => match inner.kind {
FilterKind::Literal(v) => {
assert_eq!(v, simd_json::json!("foo"));
}
_ => panic!("Expected Literal"),
},
_ => panic!("Expected Function Contains"),
}
}
#[test]
fn test_contains_array() {
let result = parse("contains([1, 2])").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Contains(inner)) => match inner.kind {
FilterKind::Array(elems) => {
assert_eq!(elems.len(), 2);
}
_ => panic!("Expected Array"),
},
_ => panic!("Expected Function Contains"),
}
}
#[test]
fn test_contains_object() {
let result = parse("contains({\"a\": 1})").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::Contains(inner)) => match inner.kind {
FilterKind::Object(pairs) => {
assert_eq!(pairs.len(), 1);
}
_ => panic!("Expected Object"),
},
_ => panic!("Expected Function Contains"),
}
}
#[test]
fn test_string_func_in_pipe() {
let result = parse(". | split(\"/\")").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Identity);
match right.kind {
FilterKind::Function(FunctionCall::Split(s)) => {
assert_eq!(s, "/");
}
_ => panic!("Expected Function Split"),
}
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_to_entries() {
assert_eq!(
parse_kind("to_entries"),
Ok(FilterKind::Builtin(Builtin::ToEntries))
);
}
#[test]
fn test_from_entries() {
assert_eq!(
parse_kind("from_entries"),
Ok(FilterKind::Builtin(Builtin::FromEntries))
);
}
#[test]
fn test_with_entries() {
let result = parse("with_entries(.)").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::WithEntries(inner)) => {
assert_eq!(inner.kind, FilterKind::Identity);
}
_ => panic!("Expected Function WithEntries"),
}
}
#[test]
fn test_with_entries_select() {
use super::super::ast::CompareOp;
let result = parse("with_entries(select(.value > 1))").unwrap();
match result.kind {
FilterKind::Function(FunctionCall::WithEntries(inner)) => match inner.kind {
FilterKind::Function(FunctionCall::Select(sel_inner)) => match sel_inner.kind {
FilterKind::Compare(left, op, _right) => {
assert_eq!(left.kind, FilterKind::Field("value".to_string()));
assert_eq!(op, CompareOp::Gt);
}
_ => panic!("Expected Compare"),
},
_ => panic!("Expected Function Select"),
},
_ => panic!("Expected Function WithEntries"),
}
}
#[test]
fn test_to_from_entries_pipe() {
let result = parse("to_entries | from_entries").unwrap();
match result.kind {
FilterKind::Pipe(left, right) => {
assert_eq!(left.kind, FilterKind::Builtin(Builtin::ToEntries));
assert_eq!(right.kind, FilterKind::Builtin(Builtin::FromEntries));
}
_ => panic!("Expected Pipe"),
}
}
#[test]
fn test_span_identity() {
let result = parse(".").unwrap();
assert_eq!(result.span.start, 0);
assert_eq!(result.span.end, 1);
}
#[test]
fn test_span_field() {
let result = parse(".foo").unwrap();
assert_eq!(result.span.start, 0);
assert_eq!(result.span.end, 4);
}
#[test]
fn test_span_with_whitespace() {
let result = parse(" .foo ").unwrap();
assert_eq!(result.span.start, 2);
assert_eq!(result.span.end, 6);
}
#[test]
fn test_span_pipe() {
let result = parse(".a | .b").unwrap();
assert_eq!(result.span.start, 0);
assert_eq!(result.span.end, 7);
}
}