cicada 1.2.2

A simple Bash-like Unix shell.
Documentation
use pest::error::Error;
use pest::iterators::Pairs;
use pest::Parser;

#[derive(Parser)]
#[grammar = "parsers/grammar.pest"]
struct Locust;

pub fn parse_lines(
    lines: &str,
) -> Result<Pairs<'_, crate::parsers::locust::Rule>, Error<crate::parsers::locust::Rule>> {
    Locust::parse(Rule::EXP, lines)
}

#[cfg(test)]
mod tests {
    use super::parse_lines;
    use super::Pairs;
    use super::Rule;

    fn _compose_pair_str(
        output: &mut String,
        lv: i32,
        pairs: Pairs<crate::parsers::locust::Rule>,
    ) {
        for pair in pairs {
            let value = pair.as_str().trim();
            if value.is_empty() {
                continue;
            }

            let rule = pair.as_rule();
            let mut i = 0;
            while i < lv {
                output.push_str("--");
                i += 1;
            }

            output.push_str(&format!("[{:?}]", rule));
            if rule == Rule::CMD || rule == Rule::TEST {
                output.push_str(&format!(" {}", value));
            }

            let pairs_new = pair.into_inner();
            _compose_pair_str(output, lv + 1, pairs_new);
        }
    }

    fn _parse_exp(lines: &str) -> String {
        let mut output = String::new();
        match parse_lines(lines) {
            Ok(x) => {
                _compose_pair_str(&mut output, 0, x);
            }
            Err(e) => {
                println!("parse error: {:?}", e);
                unreachable!();
            }
        }
        output
    }

    #[test]
    fn test_locust_parse_lines_exp_if() {
        let lines = include_str!("../../tests/locusts/if-001.sh");
        let expected = "\
            [EXP]\
            --[CMD] echo begin if exp\
            --[EXP_IF]\
            ----[IF_IF_BR]\
            ------[IF_HEAD]\
            --------[TEST] echo \"foo 中文\" | grep -qi foo\
            ------[EXP_BODY]\
            --------[CMD] echo \'we found it\'\
            ----[IF_ELSE_BR]\
            ------[KW_ELSE]\
            ------[EXP_BODY]\
            --------[CMD] echo not found\
            --[CMD] echo \"after fi\"\
            --[CMD] echo the end";
        let output = _parse_exp(lines);
        assert_eq!(output, expected);

        let lines = include_str!("../../tests/locusts/if-002.sh");
        let output = _parse_exp(lines);
        assert_eq!(output, expected);

        let lines = include_str!("../../tests/locusts/if-003.sh");
        let output = _parse_exp(lines);
        assert_eq!(output, expected);
    }

    #[test]
    fn test_locust_parse_lines_exp_if_nest() {
        let lines = include_str!("../../tests/locusts/if-nest-001.sh");
        let expected = "\
            [EXP]\
            --[EXP_IF]\
            ----[IF_IF_BR]\
            ------[IF_HEAD]\
            --------[TEST] test 001\
            ------[EXP_BODY]\
            --------[EXP_IF]\
            ----------[IF_IF_BR]\
            ------------[IF_HEAD]\
            --------------[TEST] test 002\
            ------------[EXP_BODY]\
            --------------[CMD] cmd a\
            --------------[EXP_IF]\
            ----------------[IF_IF_BR]\
            ------------------[IF_HEAD]\
            --------------------[TEST] test 003\
            ------------------[EXP_BODY]\
            --------------------[CMD] cmd b\
            ----------[IF_ELSE_BR]\
            ------------[KW_ELSE]\
            ------------[EXP_BODY]\
            --------------[CMD] cmd c\
            --------[CMD] cmd d\
            ----[IF_ELSE_BR]\
            ------[KW_ELSE]\
            ------[EXP_BODY]\
            --------[EXP_IF]\
            ----------[IF_IF_BR]\
            ------------[IF_HEAD]\
            --------------[TEST] test 004\
            ------------[EXP_BODY]\
            --------------[CMD] cmd e\
            ----------[IF_ELSE_BR]\
            ------------[KW_ELSE]\
            ------------[EXP_BODY]\
            --------------[CMD] cmd f\
            --------[CMD] cmd g";
        let output = _parse_exp(lines);
        assert_eq!(output, expected);
    }

    #[test]
    fn test_locust_parse_lines_exp_if_nest_2() {
        let lines = include_str!("../../tests/locusts/if-nest-002.sh");
        let expected = "\
            [EXP]\
            --[EXP_IF]\
            ----[IF_IF_BR]\
            ------[IF_HEAD]\
            --------[TEST] test 001\
            ------[EXP_BODY]\
            --------[CMD] cmd a\
            ----[IF_ELSE_BR]\
            ------[KW_ELSE]\
            ------[EXP_BODY]\
            --------[CMD] cmd b";
        let output = _parse_exp(lines);
        assert_eq!(output, expected);
    }

    #[test]
    fn test_locust_parse_lines_exp_else_if() {
        let lines = include_str!("../../tests/locusts/if-else-if-001.sh");
        let expected = "\
            [EXP]\
            --[EXP_IF]\
            ----[IF_IF_BR]\
            ------[IF_HEAD]\
            --------[TEST] test 1\
            ------[EXP_BODY]\
            --------[CMD] cmd a\
            ----[IF_ELSEIF_BR]\
            ------[IF_ELSEIF_HEAD]\
            --------[TEST] test 2\
            ------[EXP_BODY]\
            --------[CMD] cmd b\
            ----[IF_ELSEIF_BR]\
            ------[IF_ELSEIF_HEAD]\
            --------[TEST] test 3\
            ------[EXP_BODY]\
            --------[CMD] cmd c\
            ----[IF_ELSE_BR]\
            ------[KW_ELSE]\
            ------[EXP_BODY]\
            --------[CMD] cmd d";
        let output = _parse_exp(lines);
        assert_eq!(output, expected);
    }

    #[test]
    fn test_locust_parse_lines_exp_while() {
        let lines = include_str!("../../tests/locusts/while-001.sh");
        let expected = "\
            [EXP]\
            --[CMD] echo while begins\
            --[EXP_WHILE]\
            ----[WHILE_HEAD]\
            ------[TEST] cat /tmp/foo.txt | grep -qi foo\
            ----[EXP_BODY]\
            ------[CMD] sleep 1\
            ------[CMD] echo foo is good in while\
            --[CMD] echo the end";
        let output = _parse_exp(lines);
        assert_eq!(output, expected);

        let lines = include_str!("../../tests/locusts/while-002.sh");
        let output = _parse_exp(lines);
        assert_eq!(output, expected);

        let lines = include_str!("../../tests/locusts/while-003.sh");
        let output = _parse_exp(lines);
        assert_eq!(output, expected);

        let lines = include_str!("../../tests/locusts/while-004.sh");
        let output = _parse_exp(lines);
        assert_eq!(output, expected);
    }

    #[test]
    fn test_locust_parse_lines_exp_if_while_001() {
        let lines = include_str!("../../tests/locusts/if-while-001.sh");
        let expected = "\
            [EXP]\
            --[EXP_IF]\
            ----[IF_IF_BR]\
            ------[IF_HEAD]\
            --------[TEST] test 01\
            ------[EXP_BODY]\
            --------[CMD] cmd a\
            --------[EXP_WHILE]\
            ----------[WHILE_HEAD]\
            ------------[TEST] test 02\
            ----------[EXP_BODY]\
            ------------[CMD] cmd b\
            --------[CMD] cmd c";
        let output = _parse_exp(lines);
        assert_eq!(output, expected);
    }

    #[test]
    fn test_locust_parse_lines_exp_while_if_001() {
        let lines = include_str!("../../tests/locusts/while-if-001.sh");
        let expected = "\
            [EXP]\
            --[EXP_WHILE]\
            ----[WHILE_HEAD]\
            ------[TEST] test 01\
            ----[EXP_BODY]\
            ------[CMD] cmd a\
            ------[EXP_IF]\
            --------[IF_IF_BR]\
            ----------[IF_HEAD]\
            ------------[TEST] test 02\
            ----------[EXP_BODY]\
            ------------[CMD] cmd b\
            ------[CMD] cmd c";
        let output = _parse_exp(lines);
        assert_eq!(output, expected);
    }

    #[test]
    fn test_locust_parse_lines_exp_while_if_more_001() {
        let lines = include_str!("../../tests/locusts/while-if-more-001.sh");
        let expected = "\
            [EXP]\
            --[EXP_WHILE]\
            ----[WHILE_HEAD]\
            ------[TEST] test 01\
            ----[EXP_BODY]\
            ------[EXP_IF]\
            --------[IF_IF_BR]\
            ----------[IF_HEAD]\
            ------------[TEST] test 02\
            ----------[EXP_BODY]\
            ------------[EXP_WHILE]\
            --------------[WHILE_HEAD]\
            ----------------[TEST] test 03\
            --------------[EXP_BODY]\
            ----------------[EXP_IF]\
            ------------------[IF_IF_BR]\
            --------------------[IF_HEAD]\
            ----------------------[TEST] test 04\
            --------------------[EXP_BODY]\
            ----------------------[CMD] cmd a";
        let output = _parse_exp(lines);
        assert_eq!(output, expected);
    }

    #[test]
    fn test_locust_parse_lines_exp_while_if_more_002() {
        let lines = include_str!("../../tests/locusts/while-if-more-002.sh");
        let expected = "\
            [EXP]\
            --[EXP_IF]\
            ----[IF_IF_BR]\
            ------[IF_HEAD]\
            --------[TEST] test 01\
            ------[EXP_BODY]\
            --------[EXP_WHILE]\
            ----------[WHILE_HEAD]\
            ------------[TEST] test 02\
            ----------[EXP_BODY]\
            ------------[EXP_IF]\
            --------------[IF_IF_BR]\
            ----------------[IF_HEAD]\
            ------------------[TEST] test 03\
            ----------------[EXP_BODY]\
            ------------------[CMD] cmd a\
            ------------------[EXP_WHILE]\
            --------------------[WHILE_HEAD]\
            ----------------------[TEST] test 04\
            --------------------[EXP_BODY]\
            ----------------------[CMD] cmd b\
            ------------[CMD] cmd c\
            --------[CMD] cmd d";
        let output = _parse_exp(lines);
        assert_eq!(output, expected);
    }
}