Skip to content

Commit a635e6a

Browse files
Progress on the test functions
1 parent 2c99b9b commit a635e6a

File tree

9 files changed

+251
-83
lines changed

9 files changed

+251
-83
lines changed

jsonpath-ast/src/ast.rs

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,22 +61,23 @@ macro_rules! terminating_from_pest {
6161
use super::parse::{JSPathParser, Rule};
6262
#[cfg(feature = "compiled-path")]
6363
use crate::syn_parse::parse_impl::{
64-
ParseUtilsExt, parse_bool, parse_float, validate_function_name, validate_js_int,
65-
validate_js_str, validate_member_name_shorthand,
64+
parse_bool, parse_float, validate_js_int, validate_js_str,
65+
validate_member_name_shorthand, ParseUtilsExt,
6666
};
6767
use derive_new::new;
6868
use from_pest::{ConversionError, FromPest, Void};
69-
use pest::Parser;
7069
use pest::iterators::{Pair, Pairs};
70+
use pest::Parser;
7171
use pest_ast::FromPest;
7272
use proc_macro2::Span;
73-
#[allow(unused_imports)]
74-
use syn::LitBool;
73+
use quote::ToTokens;
7574
#[cfg(feature = "compiled-path")]
7675
use syn::parse::ParseStream;
7776
use syn::punctuated::Punctuated;
7877
use syn::token::Bracket;
79-
use syn::{Ident, Token, token};
78+
#[allow(unused_imports)]
79+
use syn::LitBool;
80+
use syn::{token, Token};
8081
#[cfg(feature = "compiled-path")]
8182
use syn_derive::Parse;
8283

@@ -611,12 +612,12 @@ impl<'pest> FromPest<'pest> for CompOp {
611612
}
612613

613614
#[derive(Debug, new, PartialEq)]
614-
#[cfg_attr(feature = "compiled-path", derive(Parse))]
615+
// #[cfg_attr(feature = "compiled-path", derive(Parse))]
615616
pub struct FunctionExpr {
616617
pub(crate) name: FunctionName,
617-
#[cfg_attr(feature = "compiled-path", syn(parenthesized))]
618+
// #[cfg_attr(feature = "compiled-path", syn(parenthesized))]
618619
pub(crate) paren: token::Paren,
619-
#[cfg_attr(feature = "compiled-path", syn(in = paren))]
620+
// #[cfg_attr(feature = "compiled-path", syn(in = paren))]
620621
// #[cfg_attr(feature = "compiled-path", parse(|i: ParseStream| PestIgnoredPunctuated::parse_terminated(i)))]
621622
pub(crate) args: PestIgnoredPunctuated<FunctionArgument, Token![,]>,
622623
}
@@ -650,9 +651,27 @@ impl<'pest> from_pest::FromPest<'pest> for FunctionExpr {
650651

651652
#[derive(Debug, new, PartialEq)]
652653
#[cfg_attr(feature = "compiled-path", derive(Parse))]
653-
pub struct FunctionName {
654-
#[cfg_attr(feature = "compiled-path", parse(validate_function_name))]
655-
name: Ident,
654+
pub enum FunctionName {
655+
#[cfg_attr(feature = "compiled-path", parse(peek = kw::length))]
656+
Length(kw::length),
657+
#[cfg_attr(feature = "compiled-path", parse(peek = kw::value))]
658+
Value(kw::value),
659+
#[cfg_attr(feature = "compiled-path", parse(peek = kw::count))]
660+
Count(kw::count),
661+
#[cfg_attr(feature = "compiled-path", parse(peek = kw::search))]
662+
Search(kw::search),
663+
#[cfg_attr(feature = "compiled-path", parse(peek = Token![match]))]
664+
Match(Token![match]),
665+
#[cfg_attr(feature = "compiled-path", parse(peek = Token![in]))]
666+
In(Token![in]),
667+
#[cfg_attr(feature = "compiled-path", parse(peek = kw::nin))]
668+
Nin(kw::nin),
669+
#[cfg_attr(feature = "compiled-path", parse(peek = kw::none_of))]
670+
NoneOf(kw::none_of),
671+
#[cfg_attr(feature = "compiled-path", parse(peek = kw::any_of))]
672+
AnyOf(kw::any_of),
673+
#[cfg_attr(feature = "compiled-path", parse(peek = kw::subset_of))]
674+
SubsetOf(kw::subset_of),
656675
}
657676

658677
impl<'pest> FromPest<'pest> for FunctionName {
@@ -666,12 +685,20 @@ impl<'pest> FromPest<'pest> for FunctionName {
666685
let pair = clone.next().ok_or(ConversionError::NoMatch)?;
667686
if matches!(
668687
pair.as_rule(),
669-
Rule::function_name_one_arg | Rule::function_name_two_arg
688+
Rule::function_name
670689
) {
671-
let mut inner = pair.into_inner();
672-
let inner = &mut inner;
673-
let this = FunctionName {
674-
name: Ident::new(inner.to_string().as_str().trim(), Span::call_site()),
690+
let this = match pair.as_str().trim() {
691+
"length" => Self::Length(Default::default()),
692+
"value" => Self::Value(Default::default()),
693+
"count" => Self::Count(Default::default()),
694+
"search" => Self::Search(Default::default()),
695+
"match" => Self::Match(Default::default()),
696+
"in" => Self::In(Default::default()),
697+
"nin" => Self::Nin(Default::default()),
698+
"none_of" => Self::NoneOf(Default::default()),
699+
"any_of" => Self::AnyOf(Default::default()),
700+
"subset_of" => Self::SubsetOf(Default::default()),
701+
_ => unreachable!("Invalid function name should be impossible, error in pest grammar")
675702
};
676703
*pest = clone;
677704
Ok(this)

jsonpath-ast/src/syn_parse.rs

Lines changed: 63 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
#[cfg(feature = "compiled-path")]
22
pub(crate) mod parse_impl {
33
use crate::ast::parse::{JSPathParser, Rule};
4+
use crate::ast::{kw, CompOp, IndexSelector, Main, NameSelector};
45
use crate::ast::{
56
AbsSingularQuery, AtomExpr, Bool, BracketName, BracketedSelection, ChildSegment, CompExpr,
6-
Comparable, DescendantSegment, EOI, FilterSelector, FunctionArgument, FunctionExpr,
7-
FunctionName, IndexSegment, JPQuery, JSInt, JSString, Literal, LogicalExpr, LogicalExprAnd,
8-
MemberNameShorthand, NameSegment, NotOp, Null, Number, ParenExpr, PestIgnoredPunctuated,
9-
PestLiteralWithoutRule, RelQuery, RelSingularQuery, Root, Segment, Segments, Selector,
10-
SingularQuery, SingularQuerySegment, SingularQuerySegments, SliceEnd, SliceSelector,
11-
SliceStart, SliceStep, Test, TestExpr, WildcardSelector,
12-
WildcardSelectorOrMemberNameShorthand,
7+
Comparable, DescendantSegment, FilterSelector, FunctionArgument, FunctionExpr, FunctionName,
8+
IndexSegment, JPQuery, JSInt, JSString, Literal, LogicalExpr, LogicalExprAnd, MemberNameShorthand,
9+
NameSegment, NotOp, Null, Number, ParenExpr, PestIgnoredPunctuated, PestLiteralWithoutRule,
10+
RelQuery, RelSingularQuery, Root, Segment, Segments, Selector, SingularQuery,
11+
SingularQuerySegment, SingularQuerySegments, SliceEnd, SliceSelector, SliceStart,
12+
SliceStep, Test, TestExpr, WildcardSelector, WildcardSelectorOrMemberNameShorthand,
13+
EOI,
1314
};
14-
use crate::ast::{CompOp, IndexSelector, Main, NameSelector, kw};
1515
use pest::Parser;
1616
use proc_macro2::{Ident, TokenStream};
17-
use quote::{ToTokens, quote};
17+
use quote::{quote, ToTokens};
1818
use syn::parse::{Parse, ParseStream};
1919
use syn::punctuated::Punctuated;
20+
use syn::spanned::Spanned;
2021
use syn::token::Token;
21-
use syn::{LitBool, LitInt, LitStr, Token, token};
22+
use syn::{token, LitBool, LitInt, LitStr, Token};
2223

2324
pub trait ParseUtilsExt: Parse {
2425
fn peek(input: ParseStream) -> bool;
@@ -629,11 +630,27 @@ pub(crate) mod parse_impl {
629630

630631
impl ToTokens for FunctionName {
631632
fn to_tokens(&self, tokens: &mut TokenStream) {
632-
tokens.extend(quote! {
633-
::jsonpath_ast::ast::FunctionName::new(
634-
::proc_macro2::Ident::new("function_name", ::proc_macro2::Span::call_site())
635-
)
636-
});
633+
// tokens.extend(quote! {
634+
// ::jsonpath_ast::ast::FunctionName::new(
635+
// ::proc_macro2::Ident::new("function_name", ::proc_macro2::Span::call_site())
636+
// )
637+
// });
638+
let variant = match self {
639+
// Literal::Number(inner) => {
640+
// quote!(new_number(#inner))
641+
// }
642+
FunctionName::Length(_) => { quote!(new_length(Default::default())) }
643+
FunctionName::Value(_) => { quote!(new_value(Default::default())) }
644+
FunctionName::Count(_) => { quote!(new_count(Default::default())) }
645+
FunctionName::Search(_) => { quote!(new_search(Default::default())) }
646+
FunctionName::Match(_) => { quote!(new_match(Default::default())) }
647+
FunctionName::In(_) => { quote!(new_in(Default::default())) }
648+
FunctionName::Nin(_) => { quote!(new_nin(Default::default())) }
649+
FunctionName::NoneOf(_) => { quote!(new_none_of(Default::default())) }
650+
FunctionName::AnyOf(_) => { quote!(new_any_of(Default::default())) }
651+
FunctionName::SubsetOf(_) => { quote!(new_subset_of(Default::default())) }
652+
};
653+
tokens.extend(quote!(::jsonpath_ast::ast::FunctionName::#variant))
637654
}
638655
}
639656

@@ -840,9 +857,13 @@ pub(crate) mod parse_impl {
840857
impl ToTokens for TestExpr {
841858
fn to_tokens(&self, tokens: &mut TokenStream) {
842859
let Self { not_op, test } = self;
860+
let repr_not = match not_op {
861+
Some(not_op) => quote! {Some(#not_op)},
862+
None => quote! {None},
863+
};
843864
tokens.extend(quote! {
844865
::jsonpath_ast::ast::TestExpr::new(
845-
#not_op,
866+
#repr_not,
846867
#test
847868
)
848869
});
@@ -992,7 +1013,11 @@ pub(crate) mod parse_impl {
9921013

9931014
impl ParseUtilsExt for CompExpr {
9941015
fn peek(input: ParseStream) -> bool {
995-
Comparable::peek(input)
1016+
let fork = input.fork();
1017+
// This is very suboptimal but the only option because at this point in the stream a comp_expr and a test_expr
1018+
// look identical if they're both functions, IE: $[?match(@, $.regex)] is a test_exp while $[?match(@, $.regex) == true]
1019+
// is a comp_exp
1020+
fork.parse::<Comparable>().is_ok() && fork.parse::<CompOp>().is_ok()
9961021
}
9971022
}
9981023
impl ParseUtilsExt for TestExpr {
@@ -1084,6 +1109,27 @@ pub(crate) mod parse_impl {
10841109
Ok(num)
10851110
}
10861111

1112+
fn function_name_expected_args(name: &FunctionName) -> (String, usize) {
1113+
(format!("{:?}", name), match name {
1114+
FunctionName::Length(_) | FunctionName::Value(_) | FunctionName::Count(_) => { 1 },
1115+
FunctionName::Search(_) | FunctionName::Match(_)
1116+
| FunctionName::In(_) | FunctionName::Nin(_)
1117+
| FunctionName::NoneOf(_) | FunctionName::AnyOf(_) | FunctionName::SubsetOf(_) => { 2 },
1118+
})
1119+
}
1120+
impl Parse for FunctionExpr {
1121+
fn parse(__input: ParseStream) -> ::syn::Result<Self> {
1122+
let paren;
1123+
let ret = Self { name: __input.parse()?, paren: syn::parenthesized!(paren in __input ), args: PestIgnoredPunctuated::parse_separated_nonempty(&paren)? };
1124+
let (func_name, expected_num_args) = function_name_expected_args(&ret.name);
1125+
if expected_num_args == ret.args.0.len() {
1126+
Ok(ret)
1127+
} else {
1128+
Err(syn::Error::new(ret.args.span(), format!("Invalid number of arguments for function {}, expected {}", func_name, expected_num_args)))
1129+
}
1130+
}
1131+
}
1132+
10871133
impl ParseUtilsExt for FunctionExpr {
10881134
fn peek(input: ParseStream) -> bool {
10891135
FunctionName::peek(input)
@@ -1105,53 +1151,6 @@ pub(crate) mod parse_impl {
11051151
}
11061152
}
11071153

1108-
pub fn validate_function_name(input: ParseStream) -> Result<Ident, syn::Error> {
1109-
if input.peek(kw::length) {
1110-
input.parse::<kw::length>()?;
1111-
return Ok(Ident::new("length", input.span()));
1112-
}
1113-
if input.peek(kw::value) {
1114-
input.parse::<kw::value>()?;
1115-
return Ok(Ident::new("value", input.span()));
1116-
}
1117-
if input.peek(kw::count) {
1118-
input.parse::<kw::count>()?;
1119-
return Ok(Ident::new("count", input.span()));
1120-
}
1121-
if input.peek(kw::search) {
1122-
input.parse::<kw::search>()?;
1123-
return Ok(Ident::new("search", input.span()));
1124-
}
1125-
if input.peek(Token![match]) {
1126-
input.parse::<Token![match]>()?;
1127-
return Ok(Ident::new("match", input.span()));
1128-
}
1129-
if input.peek(Token![in]) {
1130-
input.parse::<Token![in]>()?;
1131-
return Ok(Ident::new("in", input.span()));
1132-
}
1133-
if input.peek(kw::nin) {
1134-
input.parse::<kw::nin>()?;
1135-
return Ok(Ident::new("nin", input.span()));
1136-
}
1137-
if input.peek(kw::none_of) {
1138-
input.parse::<kw::none_of>()?;
1139-
return Ok(Ident::new("none_of", input.span()));
1140-
}
1141-
if input.peek(kw::any_of) {
1142-
input.parse::<kw::any_of>()?;
1143-
return Ok(Ident::new("any_of", input.span()));
1144-
}
1145-
if input.peek(kw::subset_of) {
1146-
input.parse::<kw::subset_of>()?;
1147-
return Ok(Ident::new("subset_of", input.span()));
1148-
}
1149-
Err(syn::Error::new(
1150-
input.span(),
1151-
"invalid function name, expected one of: length, value, count, search, match, in, nin, none_of, any_of, subset_of",
1152-
))
1153-
}
1154-
11551154
impl ParseUtilsExt for RelQuery {
11561155
fn peek(input: ParseStream) -> bool {
11571156
input.peek(Token![@])
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Test case: 00_count_function
2+
// Tags: function, count
3+
#[test]
4+
fn test_00_count_function() {
5+
let q_ast = ::jsonpath_rust_impl::json_query!($[?count(@..*)>2]);
6+
let q_pest = ::jsonpath_ast::ast::Main::try_from_pest_parse(r#"$[?count(@..*)>2]"#).expect("failed to parse");
7+
assert_eq!(q_pest, q_ast);
8+
}
9+
10+
// Test case: 01_single_node_arg
11+
// Tags: function, count
12+
#[test]
13+
fn test_01_single_node_arg() {
14+
let q_ast = ::jsonpath_rust_impl::json_query!($[?count(@.a)>1]);
15+
let q_pest = ::jsonpath_ast::ast::Main::try_from_pest_parse(r#"$[?count(@.a)>1]"#).expect("failed to parse");
16+
assert_eq!(q_pest, q_ast);
17+
}
18+
19+
// Test case: 02_multiple_selector_arg
20+
// Tags: function, count
21+
#[test]
22+
fn test_02_multiple_selector_arg() {
23+
let q_ast = ::jsonpath_rust_impl::json_query!($[?count(@["a","d"])>1]);
24+
let q_pest_double = ::jsonpath_ast::ast::Main::try_from_pest_parse(r#"$[?count(@["a","d"])>1]"#).expect("failed to parse");
25+
let q_pest_single = ::jsonpath_ast::ast::Main::try_from_pest_parse(r#"$[?count(@['a','d'])>1]"#).expect("failed to parse");
26+
assert_eq!(q_pest_double, q_ast);
27+
assert_eq!(q_pest_single, q_ast);
28+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Test case: 03_non_query_arg_number
2+
// Tags: function, count
3+
#[test]
4+
fn test_03_non_query_arg_number() {
5+
// let q_ast = ::jsonpath_rust_impl::json_query!($[?count(1)>2]);
6+
let _q_pest = ::jsonpath_ast::ast::Main::try_from_pest_parse(r#"$[?count(1)>2]"#).expect_err("should not parse");
7+
}
8+
9+
// Test case: 04_non_query_arg_string
10+
// Tags: function, count
11+
#[test]
12+
fn test_04_non_query_arg_string() {
13+
// let q_ast = ::jsonpath_rust_impl::json_query!($[?count('string')>2]);
14+
let _q_pest = ::jsonpath_ast::ast::Main::try_from_pest_parse(r#"$[?count('string')>2]"#).expect_err("should not parse");
15+
}
16+
17+
// Test case: 05_non_query_arg_true
18+
// Tags: function, count
19+
#[test]
20+
fn test_05_non_query_arg_true() {
21+
// let q_ast = ::jsonpath_rust_impl::json_query!($[?count(true)>2]);
22+
let _q_pest = ::jsonpath_ast::ast::Main::try_from_pest_parse(r#"$[?count(true)>2]"#).expect_err("should not parse");
23+
}
24+
25+
// Test case: 06_non_query_arg_false
26+
// Tags: function, count
27+
#[test]
28+
fn test_06_non_query_arg_false() {
29+
// let q_ast = ::jsonpath_rust_impl::json_query!($[?count(false)>2]);
30+
let _q_pest = ::jsonpath_ast::ast::Main::try_from_pest_parse(r#"$[?count(false)>2]"#).expect_err("should not parse");
31+
}
32+
33+
// Test case: 07_non_query_arg_null
34+
// Tags: function, count
35+
#[test]
36+
fn test_07_non_query_arg_null() {
37+
// let q_ast = ::jsonpath_rust_impl::json_query!($[?count(null)>2]);
38+
let _q_pest = ::jsonpath_ast::ast::Main::try_from_pest_parse(r#"$[?count(null)>2]"#).expect_err("should not parse");
39+
}
40+
41+
// Test case: 08_result_must_be_compared
42+
// Tags: function, count
43+
#[test]
44+
fn test_08_result_must_be_compared() {
45+
// let q_ast = ::jsonpath_rust_impl::json_query!($[?count(@..*)]);
46+
let _q_pest = ::jsonpath_ast::ast::Main::try_from_pest_parse(r#"$[?count(@..*)]"#).expect_err("should not parse");
47+
}
48+
49+
// Test case: 09_no_params
50+
// Tags: function, count
51+
#[test]
52+
fn test_09_no_params() {
53+
// let q_ast = ::jsonpath_rust_impl::json_query!($[?count()==1]);
54+
let _q_pest = ::jsonpath_ast::ast::Main::try_from_pest_parse(r#"$[?count()==1]"#).expect_err("should not parse");
55+
}
56+
57+
// Test case: 10_too_many_params
58+
// Tags: function, count
59+
#[test]
60+
fn test_10_too_many_params() {
61+
// let q_ast = ::jsonpath_rust_impl::json_query!($[?count(@.a,@.b)==1]);
62+
let _q_pest = ::jsonpath_ast::ast::Main::try_from_pest_parse(r#"$[?count(@.a,@.b)==1]"#).expect_err("should not parse");
63+
}

0 commit comments

Comments
 (0)