moduforge-rules-expression 0.5.0

moduforge 表达式规则
Documentation
use bumpalo::Bump;
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use mf_expression::functions::{DeprecatedFunction, FunctionKind};
use mf_expression::lexer::{
    Bracket, ComparisonOperator, Lexer, LogicalOperator, Operator,
};
use mf_expression::parser::{Node, Parser};

struct UnaryTest {
    src: &'static str,
    result: &'static Node<'static>,
}

const D1: Decimal = dec!(1);
const D2: Decimal = dec!(2);
const D3: Decimal = dec!(3);
const D10: Decimal = dec!(10);
const D20P5: Decimal = dec!(20.5);
const D50: Decimal = dec!(50);

#[test]
fn unary_test() {
    let tests: Vec<UnaryTest> = Vec::from([
        UnaryTest {
            src: "'str'",
            result: &Node::Binary {
                left: &Node::Identifier("$"),
                operator: Operator::Comparison(ComparisonOperator::Equal),
                right: &Node::String("str"),
            },
        },
        UnaryTest {
            src: "20.5",
            result: &Node::Binary {
                left: &Node::Identifier("$"),
                operator: Operator::Comparison(ComparisonOperator::Equal),
                right: &Node::Number(D20P5),
            },
        },
        UnaryTest {
            src: "'a', 'b', 'c'",
            result: &Node::Binary {
                operator: Operator::Logical(LogicalOperator::Or),
                left: &Node::Binary {
                    operator: Operator::Logical(LogicalOperator::Or),
                    left: &Node::Binary {
                        left: &Node::Identifier("$"),
                        operator: Operator::Comparison(
                            ComparisonOperator::Equal,
                        ),
                        right: &Node::String("a"),
                    },
                    right: &Node::Binary {
                        left: &Node::Identifier("$"),
                        operator: Operator::Comparison(
                            ComparisonOperator::Equal,
                        ),
                        right: &Node::String("b"),
                    },
                },
                right: &Node::Binary {
                    left: &Node::Identifier("$"),
                    operator: Operator::Comparison(ComparisonOperator::Equal),
                    right: &Node::String("c"),
                },
            },
        },
        UnaryTest {
            src: "[1..10]",
            result: &Node::Binary {
                operator: Operator::Comparison(ComparisonOperator::In),
                left: &Node::Identifier("$"),
                right: &Node::Interval {
                    left_bracket: Bracket::LeftSquareBracket,
                    right_bracket: Bracket::RightSquareBracket,
                    left: &Node::Number(D1),
                    right: &Node::Number(D10),
                },
            },
        },
        UnaryTest {
            src: "in [1..10]",
            result: &Node::Binary {
                operator: Operator::Comparison(ComparisonOperator::In),
                left: &Node::Identifier("$"),
                right: &Node::Interval {
                    left_bracket: Bracket::LeftSquareBracket,
                    right_bracket: Bracket::RightSquareBracket,
                    left: &Node::Number(D1),
                    right: &Node::Number(D10),
                },
            },
        },
        UnaryTest {
            src: "not in [1..10]",
            result: &Node::Binary {
                operator: Operator::Comparison(ComparisonOperator::NotIn),
                left: &Node::Identifier("$"),
                right: &Node::Interval {
                    left_bracket: Bracket::LeftSquareBracket,
                    right_bracket: Bracket::RightSquareBracket,
                    left: &Node::Number(D1),
                    right: &Node::Number(D10),
                },
            },
        },
        UnaryTest {
            src: "[1, 2, 3]",
            result: &Node::Binary {
                operator: Operator::Comparison(ComparisonOperator::In),
                left: &Node::Identifier("$"),
                right: &Node::Array(&[
                    &Node::Number(D1),
                    &Node::Number(D2),
                    &Node::Number(D3),
                ]),
            },
        },
        UnaryTest {
            src: "date('2022-01-01')",
            result: &Node::Binary {
                operator: Operator::Comparison(ComparisonOperator::Equal),
                left: &Node::Identifier("$"),
                right: &Node::FunctionCall {
                    kind: FunctionKind::Deprecated(DeprecatedFunction::Date),
                    arguments: &[&Node::String("2022-01-01")],
                },
            },
        },
        UnaryTest {
            src: "time('14:00:00')",
            result: &Node::Binary {
                operator: Operator::Comparison(ComparisonOperator::Equal),
                left: &Node::Identifier("$"),
                right: &Node::FunctionCall {
                    kind: FunctionKind::Deprecated(DeprecatedFunction::Time),
                    arguments: &[&Node::String("14:00:00")],
                },
            },
        },
        UnaryTest {
            src: "< 50",
            result: &Node::Binary {
                operator: Operator::Comparison(ComparisonOperator::LessThan),
                left: &Node::Identifier("$"),
                right: &Node::Number(D50),
            },
        },
    ]);

    let mut lexer = Lexer::new();
    let mut bump = Bump::new();

    for UnaryTest { src, result } in tests {
        let tokens = lexer.tokenize(src).unwrap();
        let parser = Parser::try_new(tokens, &bump).unwrap().unary();
        let parser_result = parser.parse();

        assert!(parser_result.error().is_ok(), "Parser failed");
        assert_eq!(parser_result.root, result);

        bump.reset();
    }
}

#[test]
fn failure_tests() {
    let tests: Vec<&str> = Vec::from(["a + b ++", "null.a", "false.b"]);

    let mut lexer = Lexer::new();
    let mut bump = Bump::new();

    for test in tests {
        let tokens = lexer.tokenize(test).unwrap();
        let unary_parser = Parser::try_new(tokens, &bump).unwrap().standard();
        let parser_result = unary_parser.parse();

        assert!(
            parser_result.error().is_err(),
            "Parsing expected to fail for: {test}"
        );

        bump.reset();
    }
}