diff --git a/filter-parser/src/lib.rs b/filter-parser/src/lib.rs index 8e21ff6be..d6fe0e889 100644 --- a/filter-parser/src/lib.rs +++ b/filter-parser/src/lib.rs @@ -382,6 +382,20 @@ fn parse_geo_point(input: Span) -> IResult { Err(nom::Err::Failure(Error::new_from_kind(input, ErrorKind::ReservedGeo("_geoPoint")))) } +/// geo = WS* "_geo(float WS* "," WS* float WS* "," WS* float) +fn parse_geo(input: Span) -> IResult { + // we want to forbid space BEFORE the _geo but not after + tuple(( + multispace0, + word_exact("_geo"), + // if we were able to parse `_geo` we are going to return a Failure whatever happens next. + cut(delimited(char('('), separated_list1(tag(","), ws(recognize_float)), char(')'))), + ))(input) + .map_err(|e| e.map(|_| Error::new_from_kind(input, ErrorKind::ReservedGeo("_geo"))))?; + // if we succeeded we still return a `Failure` because `_geo` filter is not allowed + Err(nom::Err::Failure(Error::new_from_kind(input, ErrorKind::ReservedGeo("_geo")))) +} + fn parse_error_reserved_keyword(input: Span) -> IResult { match parse_condition(input) { Ok(result) => Ok(result), @@ -418,6 +432,7 @@ fn parse_primary(input: Span, depth: usize) -> IResult { parse_not_exists, parse_to, // the next lines are only for error handling and are written at the end to have the less possible performance impact + parse_geo, parse_geo_point, parse_error_reserved_keyword, ))(input) @@ -630,6 +645,16 @@ pub mod tests { 13:34 position <= _geoPoint(12, 13, 14) "###); + insta::assert_display_snapshot!(p("_geo(12, 13, 14)"), @r###" + `_geo` is a reserved keyword and thus can't be used as a filter expression. Use the `_geoRadius(latitude, longitude, distance), or _geoBoundingBox([latitude, longitude], [latitude, longitude]) built-in rules to filter on `_geo` coordinates. + 1:17 _geo(12, 13, 14) + "###); + + insta::assert_display_snapshot!(p("position <= _geo(12, 13, 14)"), @r###" + `_geo` is a reserved keyword and thus can't be used as a filter expression. Use the `_geoRadius(latitude, longitude, distance), or _geoBoundingBox([latitude, longitude], [latitude, longitude]) built-in rules to filter on `_geo` coordinates. + 13:29 position <= _geo(12, 13, 14) + "###); + insta::assert_display_snapshot!(p("position <= _geoRadius(12, 13, 14)"), @r###" The `_geoRadius` filter is an operation and can't be used as a value. 13:35 position <= _geoRadius(12, 13, 14) diff --git a/filter-parser/src/value.rs b/filter-parser/src/value.rs index 2296c0769..9d02a7153 100644 --- a/filter-parser/src/value.rs +++ b/filter-parser/src/value.rs @@ -7,8 +7,8 @@ use nom::{InputIter, InputLength, InputTake, Slice}; use crate::error::{ExpectedValueKind, NomErrorExt}; use crate::{ - parse_geo_bounding_box, parse_geo_point, parse_geo_radius, Error, ErrorKind, IResult, Span, - Token, + parse_geo, parse_geo_bounding_box, parse_geo_point, parse_geo_radius, Error, ErrorKind, + IResult, Span, Token, }; /// This function goes through all characters in the [Span] if it finds any escaped character (`\`). @@ -88,11 +88,16 @@ pub fn parse_value(input: Span) -> IResult { // then, we want to check if the user is misusing a geo expression // This expression can’t finish without error. // We want to return an error in case of failure. - if let Err(err) = parse_geo_point(input) { - if err.is_failure() { - return Err(err); + let geo_reserved_parse_functions = [parse_geo_point, parse_geo]; + + for parser in geo_reserved_parse_functions { + if let Err(err) = parser(input) { + if err.is_failure() { + return Err(err); + } } } + match parse_geo_radius(input) { Ok(_) => { return Err(nom::Err::Failure(Error::new_from_kind(input, ErrorKind::MisusedGeoRadius)))