diff --git a/src/jsonpath/parser.rs b/src/jsonpath/parser.rs index 3a889af..52e7b83 100644 --- a/src/jsonpath/parser.rs +++ b/src/jsonpath/parser.rs @@ -408,7 +408,7 @@ fn expr_atom(input: &[u8], root_predicate: bool) -> IResult<&[u8], Expr<'_>> { } fn filter_func(input: &[u8]) -> IResult<&[u8], FilterFunc<'_>> { - alt((exists,))(input) + alt((exists, starts_with))(input) } fn exists(input: &[u8]) -> IResult<&[u8], FilterFunc<'_>> { @@ -441,6 +441,13 @@ fn exists_paths(input: &[u8]) -> IResult<&[u8], Vec>> { )(input) } +fn starts_with(input: &[u8]) -> IResult<&[u8], FilterFunc<'_>> { + preceded( + tag("starts with"), + preceded(multispace0, map(string, FilterFunc::StartsWith)), + )(input) +} + fn expr_and(input: &[u8], root_predicate: bool) -> IResult<&[u8], Expr<'_>> { map( separated_list1(delimited(multispace0, tag("&&"), multispace0), |i| { @@ -478,3 +485,18 @@ fn expr_or(input: &[u8], root_predicate: bool) -> IResult<&[u8], Expr<'_>> { }, )(input) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_starts_with() { + let input = r#"starts with "Nigel""#; + let res = starts_with(input.as_bytes()).unwrap(); + assert_eq!( + res, + (&b""[..], FilterFunc::StartsWith(Cow::Borrowed("Nigel"))) + ); + } +} diff --git a/src/jsonpath/path.rs b/src/jsonpath/path.rs index dcc0784..b5a33f9 100644 --- a/src/jsonpath/path.rs +++ b/src/jsonpath/path.rs @@ -180,6 +180,7 @@ pub enum Expr<'a> { #[derive(Debug, Clone, PartialEq)] pub enum FilterFunc<'a> { Exists(Vec>), + StartsWith(Cow<'a, str>), } impl<'a> Display for JsonPath<'a> { @@ -401,6 +402,10 @@ impl<'a> Display for Expr<'a> { } f.write_str(")")?; } + FilterFunc::StartsWith(paths) => { + f.write_str("starts with ")?; + write!(f, "{paths}")?; + } }, } Ok(()) diff --git a/src/jsonpath/selector.rs b/src/jsonpath/selector.rs index e3d0c09..39f09e9 100644 --- a/src/jsonpath/selector.rs +++ b/src/jsonpath/selector.rs @@ -490,6 +490,7 @@ impl<'a> Selector<'a> { }, Expr::FilterFunc(filter_expr) => match filter_expr { FilterFunc::Exists(paths) => self.eval_exists(root, pos, paths), + FilterFunc::StartsWith(prefix) => self.eval_starts_with(root, pos, prefix), }, _ => todo!(), } @@ -506,6 +507,16 @@ impl<'a> Selector<'a> { Ok(res) } + fn eval_starts_with( + &'a self, + _root: &'a [u8], + _pos: &Position, + _prefix: &str, + ) -> Result { + // todo + Ok(false) + } + fn convert_expr_val( &'a self, root: &'a [u8], diff --git a/tests/it/jsonpath_parser.rs b/tests/it/jsonpath_parser.rs index 928fe1a..fcd3461 100644 --- a/tests/it/jsonpath_parser.rs +++ b/tests/it/jsonpath_parser.rs @@ -62,6 +62,7 @@ fn test_json_path() { // exists expression r#"$.store.book?(exists(@.price?(@ > 20)))"#, r#"$.store?(exists(@.book?(exists(@.category?(@ == "fiction")))))"#, + r#"$.store.book?(starts with "Nigel")"#, ]; for case in cases { diff --git a/tests/it/testdata/json_path.txt b/tests/it/testdata/json_path.txt index 4a5b223..9e0fd7f 100644 --- a/tests/it/testdata/json_path.txt +++ b/tests/it/testdata/json_path.txt @@ -1219,3 +1219,28 @@ JsonPath { } +---------- Input ---------- +$.store.book?(starts with "Nigel") +---------- Output --------- +$.store.book?(starts with Nigel) +---------- AST ------------ +JsonPath { + paths: [ + Root, + DotField( + "store", + ), + DotField( + "book", + ), + FilterExpr( + FilterFunc( + StartsWith( + "Nigel", + ), + ), + ), + ], +} + +