rapidquery 0.2.6

Rapid Query Executor
Documentation
use std::convert::TryFrom;

use nom::{
    branch::alt,
    bytes::complete::tag,
    character::complete::char,
    combinator::{map, map_res},
    multi::many0,
    sequence::{delimited, preceded, tuple},
    IResult,
};

use snafu::{ensure, Snafu};

use crate::Expression;

use super::util::whitespace;
use super::Parse;

#[derive(Debug, Snafu)]
pub enum ParserErr {
    InvalidOperator,
    ParseError { message: String },
}

enum Term<Field: Parse> {
    Field(Field),
    Not(Expression<Field>),
    SubExpr(Expression<Field>),
}

impl<Field: Parse> From<Term<Field>> for Expression<Field> {
    fn from(t: Term<Field>) -> Expression<Field> {
        match t {
            Term::Field(f) => Expression::Field(f),
            Term::Not(e) => Expression::Not { not: Box::from(e) },
            Term::SubExpr(e) => e,
        }
    }
}

enum TermOperator {
    And,
    Or,
}

impl TryFrom<String> for TermOperator {
    type Error = ParserErr;

    fn try_from(v: String) -> Result<TermOperator, ParserErr> {
        match v.as_ref() {
            "&&" => Ok(TermOperator::And),
            "||" => Ok(TermOperator::Or),
            _ => Err(ParserErr::InvalidOperator),
        }
    }
}

struct ParsedExpr<Field: Parse> {
    root: Term<Field>,
    trail: Vec<(TermOperator, Term<Field>)>,
}

impl<Field: Parse> From<ParsedExpr<Field>> for Expression<Field> {
    fn from(parsed: ParsedExpr<Field>) -> Expression<Field> {
        let mut root_expr = Expression::from(parsed.root);
        for (op, term) in parsed.trail {
            match op {
                TermOperator::And => {
                    root_expr = Expression::And {
                        and: (Box::from(root_expr), Box::from(Expression::from(term))),
                    }
                }
                TermOperator::Or => {
                    root_expr = Expression::Or {
                        or: (Box::from(root_expr), Box::from(Expression::from(term))),
                    }
                }
            }
        }
        root_expr
    }
}

fn subexpr_node<Field: Parse>(i: &str) -> IResult<&str, Term<Field>> {
    map(delimited(char('('), expression, char(')')), |e| {
        Term::SubExpr(e)
    })(i)
}

fn not_node<Field: Parse>(i: &str) -> IResult<&str, Term<Field>> {
    map(preceded(char('!'), field), |expr| Term::Not(expr.into()))(i)
}

fn field<Field: Parse>(i: &str) -> IResult<&str, Term<Field>> {
    alt((
        subexpr_node,
        not_node,
        map(Field::parse, |f| Term::Field(f)),
    ))(i)
}

fn operator(i: &str) -> IResult<&str, TermOperator> {
    map_res(
        delimited(whitespace, alt((tag("&&"), tag("||"))), whitespace),
        |op_str| TermOperator::try_from(String::from(op_str)),
    )(i)
}

fn parsed_expr<Field: Parse>(i: &str) -> IResult<&str, ParsedExpr<Field>> {
    map(
        delimited(
            whitespace,
            tuple((field, many0(tuple((operator, field))))),
            whitespace,
        ),
        |(root, trail)| ParsedExpr { root, trail },
    )(i)
}

fn expression<Field: Parse>(i: &str) -> IResult<&str, Expression<Field>> {
    if i.is_empty() {
        Ok(("", Expression::default()))
    } else {
        let (rest, expr) = parsed_expr(i)?;
        Ok((rest, expr.into()))
    }
}

pub fn parse_expression<Field: Parse>(i: &str) -> Result<Expression<Field>, ParserErr> {
    let (r, expr) = expression(i).map_err(|e| ParserErr::ParseError {
        message: e.to_string(),
    })?;
    ensure!(
        r.is_empty(),
        ParseSnafu {
            message: format!("incomplete parse: [{}]", r)
        }
    );

    Ok(expr)
}