mirror of
https://github.com/meilisearch/MeiliSearch
synced 2024-11-30 08:44:27 +01:00
remove all genericity in favor of my custom error type
This commit is contained in:
parent
76a2adb7c3
commit
5d3af5f273
@ -9,10 +9,9 @@ use nom::branch::alt;
|
|||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::combinator::cut;
|
use nom::combinator::cut;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
use nom::IResult;
|
|
||||||
use Condition::*;
|
use Condition::*;
|
||||||
|
|
||||||
use crate::{parse_value, FPError, FilterCondition, Span, Token};
|
use crate::{parse_value, FilterCondition, IResult, Span, Token};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Condition<'a> {
|
pub enum Condition<'a> {
|
||||||
@ -42,9 +41,7 @@ impl<'a> Condition<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// condition = value ("==" | ">" ...) value
|
/// condition = value ("==" | ">" ...) value
|
||||||
pub fn parse_condition<'a, E: FPError<'a>>(
|
pub fn parse_condition(input: Span) -> IResult<FilterCondition> {
|
||||||
input: Span<'a>,
|
|
||||||
) -> IResult<Span<'a>, FilterCondition, E> {
|
|
||||||
let operator = alt((tag("<="), tag(">="), tag("!="), tag("<"), tag(">"), tag("=")));
|
let operator = alt((tag("<="), tag(">="), tag("!="), tag("<"), tag(">"), tag("=")));
|
||||||
let (input, (key, op, value)) = tuple((|c| parse_value(c), operator, cut(parse_value)))(input)?;
|
let (input, (key, op, value)) = tuple((|c| parse_value(c), operator, cut(parse_value)))(input)?;
|
||||||
|
|
||||||
@ -74,7 +71,7 @@ pub fn parse_condition<'a, E: FPError<'a>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// to = value value TO value
|
/// to = value value TO value
|
||||||
pub fn parse_to<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span, FilterCondition, E> {
|
pub fn parse_to(input: Span) -> IResult<FilterCondition> {
|
||||||
let (input, (key, from, _, to)) =
|
let (input, (key, from, _, to)) =
|
||||||
tuple((|c| parse_value(c), |c| parse_value(c), tag("TO"), cut(parse_value)))(input)?;
|
tuple((|c| parse_value(c), |c| parse_value(c), tag("TO"), cut(parse_value)))(input)?;
|
||||||
|
|
||||||
|
@ -37,16 +37,13 @@ use nom::error::{ContextError, ParseError};
|
|||||||
use nom::multi::{many0, separated_list1};
|
use nom::multi::{many0, separated_list1};
|
||||||
use nom::number::complete::recognize_float;
|
use nom::number::complete::recognize_float;
|
||||||
use nom::sequence::{delimited, preceded, terminated, tuple};
|
use nom::sequence::{delimited, preceded, terminated, tuple};
|
||||||
use nom::{Finish, IResult};
|
use nom::Finish;
|
||||||
use nom_locate::LocatedSpan;
|
use nom_locate::LocatedSpan;
|
||||||
pub(crate) use value::parse_value;
|
pub(crate) use value::parse_value;
|
||||||
|
|
||||||
pub type Span<'a> = LocatedSpan<&'a str, &'a str>;
|
pub type Span<'a> = LocatedSpan<&'a str, &'a str>;
|
||||||
|
|
||||||
pub trait FilterParserError<'a>: ParseError<Span<'a>> + ContextError<Span<'a>> {}
|
type IResult<'a, Ret> = nom::IResult<Span<'a>, Ret, Error<'a>>;
|
||||||
impl<'a, T> FilterParserError<'a> for T where T: ParseError<Span<'a>> + ContextError<Span<'a>> {}
|
|
||||||
|
|
||||||
use FilterParserError as FPError;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Token<'a> {
|
pub struct Token<'a> {
|
||||||
@ -96,24 +93,22 @@ impl<'a> FilterCondition<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse<E: FPError<'a>>(input: &'a str) -> Result<Self, E> {
|
pub fn parse(input: &'a str) -> Result<Self, Error> {
|
||||||
if input.trim().is_empty() {
|
if input.trim().is_empty() {
|
||||||
return Ok(Self::Empty);
|
return Ok(Self::Empty);
|
||||||
}
|
}
|
||||||
let span = Span::new_extra(input, input);
|
let span = Span::new_extra(input, input);
|
||||||
parse_filter::<'a, E>(span).finish().map(|(_rem, output)| output)
|
parse_filter(span).finish().map(|(_rem, output)| output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove OPTIONAL whitespaces before AND after the the provided parser
|
// remove OPTIONAL whitespaces before AND after the the provided parser
|
||||||
fn ws<'a, O, E: FPError<'a>>(
|
fn ws<'a, O>(inner: impl FnMut(Span<'a>) -> IResult<O>) -> impl FnMut(Span<'a>) -> IResult<O> {
|
||||||
inner: impl FnMut(Span<'a>) -> IResult<Span, O, E>,
|
|
||||||
) -> impl FnMut(Span<'a>) -> IResult<Span, O, E> {
|
|
||||||
delimited(multispace0, inner, multispace0)
|
delimited(multispace0, inner, multispace0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// and = not (~ "AND" not)*
|
/// and = not (~ "AND" not)*
|
||||||
fn parse_or<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span, FilterCondition, E> {
|
fn parse_or(input: Span) -> IResult<FilterCondition> {
|
||||||
let (input, lhs) = parse_and(input)?;
|
let (input, lhs) = parse_and(input)?;
|
||||||
let (input, ors) = many0(preceded(ws(tag("OR")), cut(parse_and)))(input)?;
|
let (input, ors) = many0(preceded(ws(tag("OR")), cut(parse_and)))(input)?;
|
||||||
|
|
||||||
@ -123,7 +118,7 @@ fn parse_or<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span, FilterConditio
|
|||||||
Ok((input, expr))
|
Ok((input, expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_and<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span, FilterCondition, E> {
|
fn parse_and(input: Span) -> IResult<FilterCondition> {
|
||||||
let (input, lhs) = parse_not(input)?;
|
let (input, lhs) = parse_not(input)?;
|
||||||
let (input, ors) = many0(preceded(ws(tag("AND")), cut(parse_not)))(input)?;
|
let (input, ors) = many0(preceded(ws(tag("AND")), cut(parse_not)))(input)?;
|
||||||
let expr = ors
|
let expr = ors
|
||||||
@ -133,7 +128,7 @@ fn parse_and<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span, FilterConditi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// not = ("NOT" | "!") not | primary
|
/// not = ("NOT" | "!") not | primary
|
||||||
fn parse_not<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span, FilterCondition, E> {
|
fn parse_not(input: Span) -> IResult<FilterCondition> {
|
||||||
alt((
|
alt((
|
||||||
map(preceded(alt((tag("!"), tag("NOT"))), cut(parse_not)), |e| e.negate()),
|
map(preceded(alt((tag("!"), tag("NOT"))), cut(parse_not)), |e| e.negate()),
|
||||||
cut(parse_primary),
|
cut(parse_primary),
|
||||||
@ -141,7 +136,7 @@ fn parse_not<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span, FilterConditi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// geoRadius = WS* ~ "_geoRadius(float ~ "," ~ float ~ "," float)
|
/// geoRadius = WS* ~ "_geoRadius(float ~ "," ~ float ~ "," float)
|
||||||
fn parse_geo_radius<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span<'a>, FilterCondition, E> {
|
fn parse_geo_radius(input: Span) -> IResult<FilterCondition> {
|
||||||
let err_msg_args_incomplete = "_geoRadius. The `_geoRadius` filter expect three arguments: `_geoRadius(latitude, longitude, radius)`";
|
let err_msg_args_incomplete = "_geoRadius. The `_geoRadius` filter expect three arguments: `_geoRadius(latitude, longitude, radius)`";
|
||||||
|
|
||||||
// we want to forbid space BEFORE the _geoRadius but not after
|
// we want to forbid space BEFORE the _geoRadius but not after
|
||||||
@ -153,8 +148,8 @@ fn parse_geo_radius<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span<'a>, Fi
|
|||||||
let (input, args): (Span, Vec<Span>) = parsed?;
|
let (input, args): (Span, Vec<Span>) = parsed?;
|
||||||
|
|
||||||
if args.len() != 3 {
|
if args.len() != 3 {
|
||||||
let e = E::from_char(input, '(');
|
let e = Error::from_char(input, '(');
|
||||||
return Err(nom::Err::Failure(E::add_context(input, err_msg_args_incomplete, e)));
|
return Err(nom::Err::Failure(Error::add_context(input, err_msg_args_incomplete, e)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = FilterCondition::GeoLowerThan {
|
let res = FilterCondition::GeoLowerThan {
|
||||||
@ -165,7 +160,7 @@ fn parse_geo_radius<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span<'a>, Fi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// primary = (WS* ~ "(" expression ")" ~ WS*) | geoRadius | condition | to
|
/// primary = (WS* ~ "(" expression ")" ~ WS*) | geoRadius | condition | to
|
||||||
fn parse_primary<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span, FilterCondition, E> {
|
fn parse_primary(input: Span) -> IResult<FilterCondition> {
|
||||||
alt((
|
alt((
|
||||||
delimited(ws(char('(')), cut(parse_expression), cut(ws(char(')')))),
|
delimited(ws(char('(')), cut(parse_expression), cut(ws(char(')')))),
|
||||||
|c| parse_geo_radius(c),
|
|c| parse_geo_radius(c),
|
||||||
@ -175,12 +170,12 @@ fn parse_primary<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span, FilterCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// expression = or
|
/// expression = or
|
||||||
pub fn parse_expression<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span, FilterCondition, E> {
|
pub fn parse_expression(input: Span) -> IResult<FilterCondition> {
|
||||||
parse_or(input)
|
parse_or(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// filter = expression ~ EOF
|
/// filter = expression ~ EOF
|
||||||
pub fn parse_filter<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span, FilterCondition, E> {
|
pub fn parse_filter(input: Span) -> IResult<FilterCondition> {
|
||||||
terminated(parse_expression, eof)(input)
|
terminated(parse_expression, eof)(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,7 +467,7 @@ pub mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for (input, expected) in test_case {
|
for (input, expected) in test_case {
|
||||||
let result = Fc::parse::<Error<Span>>(input);
|
let result = Fc::parse(input);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
result.is_ok(),
|
result.is_ok(),
|
||||||
@ -489,22 +484,22 @@ pub mod tests {
|
|||||||
fn error() {
|
fn error() {
|
||||||
use FilterCondition as Fc;
|
use FilterCondition as Fc;
|
||||||
|
|
||||||
let result = Fc::parse::<crate::Error<Span>>("test = truc OR truc");
|
let result = Fc::parse("test = truc OR truc");
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
|
|
||||||
let test_case = [
|
let test_case = [
|
||||||
// simple test
|
// simple test
|
||||||
|
("channel = Ponce = 12", "An error occured"),
|
||||||
("OR", "An error occured"),
|
("OR", "An error occured"),
|
||||||
("AND", "An error occured"),
|
("AND", "An error occured"),
|
||||||
("channel = Ponce OR", "An error occured"),
|
("channel = Ponce OR", "An error occured"),
|
||||||
("channel = Ponce = 12", "An error occured"),
|
|
||||||
("_geoRadius = 12", "An error occured"),
|
("_geoRadius = 12", "An error occured"),
|
||||||
("_geoPoint(12, 13, 14)", "An error occured"),
|
("_geoPoint(12, 13, 14)", "An error occured"),
|
||||||
("_geo = _geoRadius(12, 13, 14)", "An error occured"),
|
("_geo = _geoRadius(12, 13, 14)", "An error occured"),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (input, expected) in test_case {
|
for (input, expected) in test_case {
|
||||||
let result = Fc::parse::<Error<Span>>(input);
|
let result = Fc::parse(input);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
result.is_err(),
|
result.is_err(),
|
||||||
|
@ -2,25 +2,25 @@ use nom::branch::alt;
|
|||||||
use nom::bytes::complete::{take_till, take_while1};
|
use nom::bytes::complete::{take_till, take_while1};
|
||||||
use nom::character::complete::char;
|
use nom::character::complete::char;
|
||||||
use nom::sequence::delimited;
|
use nom::sequence::delimited;
|
||||||
use nom::IResult;
|
|
||||||
|
|
||||||
use crate::{ws, FPError, Span, Token};
|
use crate::{ws, Error, IResult, Span, Token};
|
||||||
|
|
||||||
/// value = WS* ~ ( word | singleQuoted | doubleQuoted) ~ WS*
|
/// value = WS* ~ ( word | singleQuoted | doubleQuoted) ~ WS*
|
||||||
pub fn parse_value<'a, E: FPError<'a>>(input: Span<'a>) -> IResult<Span<'a>, Token, E> {
|
pub fn parse_value(input: Span) -> IResult<Token> {
|
||||||
// singleQuoted = "'" .* all but quotes "'"
|
// singleQuoted = "'" .* all but quotes "'"
|
||||||
let simple_quoted_key = |input| take_till(|c: char| c == '\'')(input);
|
let simple_quoted = |input| take_till(|c: char| c == '\'')(input);
|
||||||
// doubleQuoted = "\"" (word | spaces)* "\""
|
// doubleQuoted = "\"" (word | spaces)* "\""
|
||||||
let quoted_key = |input| take_till(|c: char| c == '"')(input);
|
let double_quoted = |input| take_till(|c: char| c == '"')(input);
|
||||||
// word = (alphanumeric | _ | - | .)+
|
// word = (alphanumeric | _ | - | .)+
|
||||||
let word = |input| take_while1(is_key_component)(input);
|
let word = |input| take_while1(is_key_component)(input);
|
||||||
|
|
||||||
alt((
|
ws(alt((
|
||||||
ws(delimited(char('\''), simple_quoted_key, char('\''))),
|
delimited(char('\''), simple_quoted, char('\'')),
|
||||||
ws(delimited(char('"'), quoted_key, char('"'))),
|
delimited(char('"'), double_quoted, char('"')),
|
||||||
ws(word),
|
word,
|
||||||
))(input)
|
)))(input)
|
||||||
.map(|(s, t)| (s, t.into()))
|
.map(|(s, t)| (s, t.into()))
|
||||||
|
.map_err(|e| e.map(|_| Error::expected_value(input)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_key_component(c: char) -> bool {
|
fn is_key_component(c: char) -> bool {
|
||||||
@ -29,8 +29,6 @@ fn is_key_component(c: char) -> bool {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use nom::error::Error;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::tests::rtok;
|
use crate::tests::rtok;
|
||||||
|
|
||||||
@ -58,7 +56,7 @@ pub mod tests {
|
|||||||
|
|
||||||
for (input, expected) in test_case {
|
for (input, expected) in test_case {
|
||||||
let input = Span::new_extra(input, input);
|
let input = Span::new_extra(input, input);
|
||||||
let result = parse_value::<Error<Span>>(input);
|
let result = parse_value(input);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
result.is_ok(),
|
result.is_ok(),
|
||||||
|
Loading…
Reference in New Issue
Block a user