slash-lang 0.1.0

Parser and AST for the slash-command language
Documentation
use slash_lang::parser::errors::ParseError;
use slash_lang::parser::parse;

#[test]
fn invalid_bang_position() {
    let err = parse("/a ! /b").unwrap_err();
    assert_eq!(
        err,
        ParseError::InvalidBang {
            token: "!".to_string(),
            position: 1
        }
    );
}

#[test]
fn invalid_bang_too_many_exclamations() {
    let err = parse("/a!!!!").unwrap_err();
    assert_eq!(
        err,
        ParseError::InvalidBang {
            token: "/a!!!!".to_string(),
            position: 0
        }
    );
}

#[test]
fn missing_operator_two_commands() {
    let err = parse("/a /b").unwrap_err();
    assert_eq!(
        err,
        ParseError::MissingOperator {
            token: "/b".to_string(),
            position: 1
        }
    );
}

#[test]
fn invalid_redirection_no_target() {
    let err = parse("/a >").unwrap_err();
    assert_eq!(
        err,
        ParseError::InvalidRedirection {
            token: ">".to_string(),
            position: 1
        }
    );
}

#[test]
fn invalid_suffix_double_optional() {
    let err = parse("/a??").unwrap_err();
    assert_eq!(
        err,
        ParseError::InvalidSuffix {
            token: "/a??".to_string(),
            position: 0
        }
    );
}

#[test]
fn invalid_digits_in_command() {
    let err = parse("/a3b").unwrap_err();
    assert_eq!(
        err,
        ParseError::InvalidDigits {
            token: "/a3b".to_string(),
            position: 0
        }
    );
}

#[test]
fn malformed_chain_unmatched_paren() {
    let err = parse("/build.flag(unclosed").unwrap_err();
    assert_eq!(
        err,
        ParseError::MalformedChain {
            token: "/build.flag(unclosed".to_string(),
            position: 0
        }
    );
}

#[test]
fn invalid_redirection_operator_as_target() {
    let err = parse("/a > &&").unwrap_err();
    assert_eq!(
        err,
        ParseError::InvalidRedirection {
            token: ">".to_string(),
            position: 1
        }
    );
}

#[test]
fn missing_operator_leading_pipe() {
    let err = parse("| /a").unwrap_err();
    assert_eq!(
        err,
        ParseError::MissingOperator {
            token: "|".to_string(),
            position: 0
        }
    );
}

#[test]
fn display_includes_token_and_position() {
    let err = parse("/a ! /b").unwrap_err();
    let msg = err.to_string();
    assert!(
        msg.contains("position 1"),
        "expected 'position 1' in: {msg}"
    );
    assert!(msg.contains('!'), "expected token in: {msg}");
}