bevy_state_plugin_generator 1.4.4

A build-dependency that generates a Bevy State Plugin from a simple state definition.
Documentation
use nom::Parser;
use nom::branch::alt;
use speculoos::prelude::*;

use crate::parsing::parsers::*;
use crate::parsing::*;
use crate::testing::*;

#[rstest]
#[case("Name", "Name")]
#[case("Name ", "Name")]
#[case("Name123", "Name123")]
#[case("Name_123", "Name_123")]
fn test_identifier(#[case] input: &str, #[case] token: &str) {
    assert_that!(identifier(input))
        .is_ok()
        .map(|(_, token)| token)
        .is_equal_to(Identifier::from(token));
}

#[rstest]
#[case("//\nHello\n", "")]
#[case("//Hello\n", "Hello")]
#[case("// Hello\n", "Hello")]
#[case("// Hello// \n", "Hello//")]
#[case("// Hello, how are you? \n", "Hello, how are you?")]
fn test_comment(#[case] input: &str, #[case] expected: &str) {
    assert_that!(comment(input))
        .is_ok()
        .map(|(_, token)| token)
        .is_equal_to(Comment::from(expected));
}

#[rstest]
#[case("//Hello\n", "Hello")]
#[case("// Hello\n", "Hello")]
#[case("// Hello// \n", "Hello//")]
#[case("// Hello, how are you? \n", "Hello, how are you?")]
fn test_parse_comment(#[case] input: &str, #[case] expected: &str) {
    assert_that!(parse_comment(input))
        .is_ok()
        .map(|(_, token)| token)
        .is_equal_to(Node::comment(expected));
}

#[rstest]
#[case("{", Token::OpenEnum)]
#[case("}", Token::CloseEnum)]
#[case("[", Token::OpenList)]
#[case("]", Token::CloseList)]
fn test_single_char_tokens(#[case] input: &str, #[case] expected: Token) {
    let mut parser = alt((open_enum, close_enum, open_list, close_list));

    assert_that!(parser.parse(input))
        .is_ok()
        .is_equal_to(("", expected));
}

#[rstest]
#[case("Root,", ",", Node::singleton("Root"))]
#[case("  Root,", ",", Node::singleton("Root"))]
#[case("First, Second", ", Second", Node::singleton("First"))]
fn test_parse_singleton(#[case] input: &str, #[case] rest: &str, #[case] node: Node) {
    assert_that!(parse_singleton(input).unwrap()).is_equal_to((rest, node));
}

#[rstest]
fn test_parse_enum_empty() {
    assert_compact_debug_snapshot!(parse_enum("Root{}").unwrap(), @r#"("", Enum(Identifier("Root"), []))"#);
    assert_compact_debug_snapshot!(parse_enum("Root {}").unwrap(), @r#"("", Enum(Identifier("Root"), []))"#);
}

#[rstest]
#[case("Root {A}", node::enum_root_a())]
#[case("Root { A}", node::enum_root_a())]
#[case("Root {A }", node::enum_root_a())]
#[case("Root { A }", node::enum_root_a())]
fn test_parse_enum_single(#[case] input: &str, #[case] node: Node) {
    assert_that!(parse_enum(input).unwrap()).is_equal_to(("", node));
}

#[rstest]
#[case("Root {A,B}", node::enum_root_ab())]
#[case("Root { A B }", node::enum_root_ab())]
#[case("Root { A { B } }", node::enum_root_a_b())]
#[case("Root { A { B } C }", node::enum_root_a_b_up_c())]
#[case("Root { A { B }, C }", node::enum_root_a_b_up_c())]
fn test_parse_enum_variants(#[case] input: &str, #[case] node: Node) {
    assert_that!(parse_enum(input).unwrap()).is_equal_to(("", node));
}

#[rstest]
#[case::just_a_comma("Root {,}", Node::enumeration("Root", [ ]))]
#[case::mora_commas("Root {,,,,}", Node::enumeration("Root", [ ]))]
#[case::comma_after_variant("Root {A,}", node::enum_root_a())]
#[case::comma_before_variant("Root {,A}", node::enum_root_a())]
#[case::comma_between_variants("Root {A,B}", node::enum_root_ab())]
fn test_parse_enum_optional_commas(#[case] input: &str, #[case] node: Node) {
    assert_that!(parse_enum(input).unwrap()).is_equal_to(("", node));
}

#[rstest]
#[case("Root []", Node::list_empty("Root"))]
#[case("Root[]", Node::list_empty("Root"))]
#[case("Root[A]", node::list_root_a())]
#[case("Root[A,B]", node::list_root_ab())]
fn test_parse_list(#[case] input: &str, #[case] node: Node) {
    assert_that!(parse_list(input).unwrap()).is_equal_to(("", node));
}

#[rstest]
#[case("Root")]
#[case("Root {A}")]
fn test_parse_node(#[case] input: &str) {
    set_snapshot_suffix!("{}", input.replace(" ", "_"));
    assert_compact_debug_snapshot!(parse_node(input));
}

#[rstest]
fn test_parse_node_nested_enums() {
    let input = "Root { A { B, C {D E {F G}} H } I J }";
    assert_debug_snapshot!(parse_node(input));
}

#[rstest]
#[case("//Comment", Node::comment("Comment"))]
#[case("// Comment", Node::comment("Comment"))]
fn test_parse_node_with_comments(#[case] input: &str, #[case] comment: Node) {
    assert_that!(parse_node(input)).is_ok_containing(("", comment));
}

#[rstest]
fn test_parse_node_messy_example() {
    let input = "Root [ A { B, C [D E {F G}] H } I J ]";
    assert_debug_snapshot!(parse_node(input));
}

#[rstest]
fn test_parse_list_incomplete() {
    assert_compact_debug_snapshot!(parse_list("Root [ A"), @r#"Err(Error(Error { input: "", code: Tag })) "#);
}

#[rstest]
fn test_parse_enum_incomplete() {
    assert_compact_debug_snapshot!(parse_enum("Root { A"), @r#"Err(Error(Error { input: "", code: Tag }))"#);
}

#[rstest]
#[case("A", vec![Node::singleton("A")])]
#[case("A B", vec![Node::singleton("A"), Node::singleton("B")] )]
#[case("A,B", vec![Node::singleton("A"), Node::singleton("B")] )]
#[case("A, B", vec![Node::singleton("A"), Node::singleton("B")] )]
#[case("A{B}", vec![Node::enumeration("A", [ Node::singleton("B") ])] )]
#[case("A{B,C D,}", vec![Node::enumeration("A", [
    Node::singleton("B"),
    Node::singleton("C"),
    Node::singleton("D")
])])]
#[case("A{B{C{D{E{F}}}}}", vec![Node::enumeration("A", [
    Node::enumeration("B", [
        Node::enumeration("C", [
            Node::enumeration("D", [
                Node::enumeration("E", [
                    Node::singleton("F")
                ])
            ])
        ])
    ])
])])]
#[ case("//A//{ B C }", vec![ Node::comment("A//{ B C }") ])]
#[ case("A//{ B C }", vec![
    Node::singleton("A"),
    Node::comment("{ B C }")
])]
fn test_parse_config(#[case] input: &str, #[case] expected: Vec<Node>) {
    assert_that!(parse_config(input))
        .named(&format!("\"{input}\""))
        .is_ok()
        .is_equal_to(("", expected));
}

#[rstest]
#[case("A//\n{ B C }", vec![
    Node::singleton("A"),
    Node::comment("")
], "{ B C }")]
#[case("A {\n // B\n C\n}...", vec![ Node::enumeration("A", [
    Node::comment("B"),
    Node::singleton("C")
]) ], "...")]
fn test_parse_config_incomplete(
    #[case] input: &str,
    #[case] expected: Vec<Node>,
    #[case] rest: &str,
) {
    assert_that!(parse_config(input))
        .named(&format!("\"{input}\""))
        .is_ok()
        .is_equal_to((rest, expected));
}

#[rstest]
#[case("Main", Node::singleton("Main"))]
#[case("Main{}", Node::Enum("Main".into(), Default::default()))]
#[case("Main{A}", Node::enumeration("Main", [Node::singleton("A")]))]
#[case("Main{A,B}", Node::enumeration("Main", [
    Node::singleton("A"),
    Node::singleton("B"),
]))]
fn test_parse_node_try_from_str(#[case] input: &str, #[case] expected: Node) {
    assert_that!(Node::try_from(input)).is_ok_containing(expected);
}

#[rstest]
#[case("RootState", 1)]
#[case("Root, Root2", 2)]
#[case("A B C D E F G H I", 9)]
#[case("A { B [C] } D { E F [ G H ] I }", 2)]
fn test_parse_state_text(#[case] source: &str, #[case] root_count: usize) {
    let (_, parse_nodes) = parse_config(source).unwrap();
    assert_that!(parse_nodes).has_length(root_count);
}