ranty 1.0.0

The Ranty procedural templating language
Documentation
mod common;

use common::{compile_with_reporter, run_str};

#[test]
fn attribute_accessors_can_round_trip_mutable_attribute_state() {
    assert_eq!(run_str(r#"<@rep = 3>[rep:<@rep>]{x}"#), "xxx");
    assert_eq!(run_str(r#"<@sep = ",">[rep:3][sep:<@sep>]{x}"#), "x,x,x");
    assert_eq!(
        run_str(r#"<@sel = "forward">[rep:4][sel:<@sel>]{a|b}"#),
        "abab"
    );
    assert_eq!(
        run_str(
            r#"
<$mutator = [?: elem] { [elem]! }>
<@sel = "forward">
<@mut = <mutator>>
[rep: all][sel:<@sel>][mut:<@mut>]{a|b}
"#,
        ),
        "a!b!"
    );
}

#[test]
fn attribute_keywords_are_readable_as_plain_expressions() {
    assert_eq!(run_str(r#"[rep:3]{[eq: @step; 0] @break}"#), "@true");
    assert_eq!(run_str(r#"[rep:3]{[neq: @step; [step]] @break}"#), "@true");
    assert_eq!(run_str(r#"[rep:3]{[eq: @total; 3] @break}"#), "@true");
    assert_eq!(
        run_str(r#"[rep: forever]{[eq: @total; <>] @break}"#),
        "@true"
    );
}

#[test]
fn attribute_keyword_block_sugar_applies_to_immediate_blocks() {
    assert_eq!(run_str(r#"@rep 3: {x}"#), "xxx");
    assert_eq!(run_str(r#"@sel "forward": {a|b}"#), "a");
    assert_eq!(run_str(r#"@mut [?: elem] { [elem]! }: {foo}"#), "foo!");
}

#[test]
fn read_only_attribute_keywords_cannot_be_assigned() {
    let (result, messages) = compile_with_reporter("<@step = 1>");
    assert!(result.is_err(), "assigning to @step should fail");
    assert!(messages.iter().any(|msg| msg.code() == "R0206"));

    let (result, messages) = compile_with_reporter("@total 1: {x}");
    assert!(result.is_err(), "block sugar on @total should fail");
    assert!(messages.iter().any(|msg| msg.code() == "R0206"));
}

#[test]
fn attribute_keywords_reject_unsupported_accessor_forms() {
    let (result, messages) = compile_with_reporter("<@rep += 1>");
    assert!(result.is_err(), "compound assignment on @rep should fail");
    assert!(messages.iter().any(|msg| msg.code() == "R0205"));
}