ranty 1.0.0

The Ranty procedural templating language
Documentation
mod common;

use assert_matches::assert_matches;
use common::{run, run_str};
use ranty::runtime::{RuntimeError, RuntimeErrorType};

#[test]
fn inherited_map_value_lookup() {
    assert_eq!(
        run_str(
            r#"
            <$obj = (::)>
            <$proto = (:: flavor = vanilla)>
            [set-proto: <obj>; <proto>]
            <obj/flavor>
            "#
        ),
        "vanilla"
    );
}

#[test]
fn own_map_values_shadow_prototype_values() {
    assert_eq!(
        run_str(
            r#"
            <$obj = (:: flavor = chocolate)>
            <$proto = (:: flavor = vanilla)>
            [set-proto: <obj>; <proto>]
            <obj/flavor>
            "#
        ),
        "chocolate"
    );
}

#[test]
fn multi_hop_prototype_lookup_works() {
    assert_eq!(
        run_str(
            r#"
            <$obj = (::)>
            <$proto = (::)>
            <$base = (:: flavor = mint)>
            [set-proto: <proto>; <base>]
            [set-proto: <obj>; <proto>]
            <obj/flavor>
            "#
        ),
        "mint"
    );
}

#[test]
fn inherited_functions_are_callable() {
    assert_eq!(
        run_str(
            r#"
            <$obj = (::)>
            <$proto = (:: greet = [?: name] { Hello,\s<name>! })>
            [set-proto: <obj>; <proto>]
            [obj/greet: Ranty]
            "#
        ),
        "Hello, Ranty!"
    );
}

#[test]
fn getter_fallback_uses_full_prototype_chain() {
    assert_eq!(
        run_str(
            r#"
            <$obj = (::)>
            <$proto = (:: flavor = vanilla)>
            [set-proto: <obj>; <proto>]
            <obj/flavor ? oops>, <obj/missing ? oops>
            "#
        ),
        "vanilla, oops"
    );
}

#[test]
fn writing_to_inherited_key_stays_local() {
    assert_eq!(
        run_str(
            r#"
            <$obj = (::)>
            <$proto = (:: flavor = vanilla)>
            [set-proto: <obj>; <proto>]
            <obj/flavor = chocolate>
            <obj/flavor>, <proto/flavor>
            "#
        ),
        "chocolate, vanilla"
    );
}

#[test]
fn removing_and_taking_only_affect_local_keys() {
    assert_eq!(
        run_str(
            r#"
            <$obj = (:: flavor = chocolate; local = here)>
            <$proto = (:: flavor = vanilla; inherited = there)>
            [set-proto: <obj>; <proto>]
            [remove: <obj>; flavor]
            [take: <obj>; local], <obj/flavor>, <proto/flavor>, <obj/inherited ? missing>, <obj/local ? missing>
            "#
        ),
        "here, vanilla, vanilla, there, missing"
    );
}

#[test]
fn lookup_only_utilities_remain_own_only() {
    assert_eq!(
        run_str(
            r#"
            <$obj = (:: own = 1)>
            <$proto = (:: inherited = 2)>
            [set-proto: <obj>; <proto>]
            [has: <obj>; own]\n
            [has: <obj>; inherited]\n
            [len: [keys: <obj>]]\n
            [len: [values: <obj>]]\n
            [translate: (: own; inherited); <obj>]\n
            <obj>
            "#
        ),
        "@true\n@false\n1\n1\n(: 1; inherited)\n(:: own = 1)"
    );
}

#[test]
fn direct_prototype_cycles_are_rejected() {
    assert_matches!(
        run(r#"
            <$obj = (::)>
            [set-proto: <obj>; <obj>]
            "#),
        Err(RuntimeError {
            error_type: RuntimeErrorType::ArgumentError,
            ..
        })
    );
}

#[test]
fn indirect_prototype_cycles_are_rejected() {
    assert_matches!(
        run(r#"
            <$obj = (::)>
            <$proto = (::)>
            <$base = (::)>
            [set-proto: <obj>; <proto>]
            [set-proto: <proto>; <base>]
            [set-proto: <base>; <obj>]
            "#),
        Err(RuntimeError {
            error_type: RuntimeErrorType::ArgumentError,
            ..
        })
    );
}