slash-lang 0.1.0

Parser and AST for the slash-command language
Documentation
use slash_lang::parser::ast::{Arg, Priority, Urgency};
use slash_lang::parser::errors::ParseError;
use slash_lang::parser::parse;

fn arg(name: &str, value: Option<&str>) -> Arg {
    Arg {
        name: name.to_string(),
        value: value.map(str::to_string),
    }
}

#[test]
fn parses_no_args() {
    let prog = parse("/build").unwrap();
    let cmd = &prog.pipelines[0].commands[0];
    assert_eq!(cmd.name, "build");
    assert!(cmd.args.is_empty());
}

#[test]
fn parses_single_arg() {
    let prog = parse("/build.flag(value)").unwrap();
    let cmd = &prog.pipelines[0].commands[0];
    assert_eq!(cmd.name, "build");
    assert_eq!(cmd.args, [arg("flag", Some("value"))]);
}

#[test]
fn parses_multiple_args() {
    let prog = parse("/build.flag1(val1).flag2(val2)").unwrap();
    let cmd = &prog.pipelines[0].commands[0];
    assert_eq!(cmd.name, "build");
    assert_eq!(
        cmd.args,
        [arg("flag1", Some("val1")), arg("flag2", Some("val2"))]
    );
}

#[test]
fn parses_empty_parens_as_no_value() {
    let prog = parse("/build.verbose()").unwrap();
    let cmd = &prog.pipelines[0].commands[0];
    assert_eq!(cmd.args, [arg("verbose", None)]);
}

#[test]
fn parses_bare_method_as_no_value() {
    let prog = parse("/build.verbose").unwrap();
    let cmd = &prog.pipelines[0].commands[0];
    assert_eq!(cmd.args, [arg("verbose", None)]);
}

#[test]
fn priority_inferred_from_command_name_not_chain() {
    let prog = parse("/SomeFoo.flag(val)").unwrap();
    let cmd = &prog.pipelines[0].commands[0];
    assert_eq!(cmd.priority, Priority::High);
    assert_eq!(cmd.name, "somefoo");
}

#[test]
fn test_id_from_command_name_not_chain() {
    let prog = parse("/test3.flag(val)").unwrap();
    let cmd = &prog.pipelines[0].commands[0];
    assert_eq!(cmd.test_id, Some(3));
}

#[test]
fn args_with_urgency_and_optional() {
    let prog = parse("/Deploy.env(prod)?!!").unwrap();
    let cmd = &prog.pipelines[0].commands[0];
    assert_eq!(cmd.name, "deploy");
    assert_eq!(cmd.args, [arg("env", Some("prod"))]);
    assert!(cmd.optional);
    assert_eq!(cmd.urgency, Urgency::Medium);
    assert_eq!(cmd.priority, Priority::High);
}

#[test]
fn value_with_dot_is_single_arg() {
    let prog = parse("/build.version(1.0)").unwrap();
    let cmd = &prog.pipelines[0].commands[0];
    assert_eq!(cmd.args, [arg("version", Some("1.0"))]);
}

#[test]
fn unmatched_open_paren_is_error() {
    assert_eq!(
        parse("/build.flag(val"),
        Err(ParseError::MalformedChain {
            token: "/build.flag(val".to_string(),
            position: 0
        })
    );
}

#[test]
fn unmatched_close_paren_is_error() {
    assert_eq!(
        parse("/build.flag)val("),
        Err(ParseError::MalformedChain {
            token: "/build.flag)val(".to_string(),
            position: 0
        })
    );
}

#[test]
fn extra_close_paren_in_value_is_error() {
    assert_eq!(
        parse("/build.flag(val))"),
        Err(ParseError::MalformedChain {
            token: "/build.flag(val))".to_string(),
            position: 0
        })
    );
}