hamelin_eval 0.11.1

Expression evaluation for Hamelin query language
Documentation
use crate::value::Value;
use hamelin_lib::tree::ast::identifier::SimpleIdentifier;
use hamelin_lib::tree::builder::{boolean, field, field_ref, int, string, struct_literal};
use hamelin_lib::types::{INT, STRING};
use ordermap::OrderMap;

use super::test_helpers::{test_context, TestContext};

#[test]
fn test_eval_struct_literal() {
    let ctx = test_context();

    // Simple struct with two fields
    let expr = struct_literal()
        .field("name", string("Alice"))
        .field("age", int(30));
    let result = ctx.eval_expr(&expr);

    let mut expected_fields = OrderMap::new();
    expected_fields.insert(
        "name".parse::<SimpleIdentifier>().unwrap(),
        Value::String("Alice".to_string()),
    );
    expected_fields.insert("age".parse::<SimpleIdentifier>().unwrap(), Value::Int(30));
    assert_eq!(result, Value::Struct(expected_fields));

    // Nested struct
    let expr = struct_literal()
        .field(
            "user",
            struct_literal()
                .field("id", int(1))
                .field("name", string("Bob")),
        )
        .field("score", int(95));
    let result = ctx.eval_expr(&expr);

    let mut inner_fields = OrderMap::new();
    inner_fields.insert("id".parse::<SimpleIdentifier>().unwrap(), Value::Int(1));
    inner_fields.insert(
        "name".parse::<SimpleIdentifier>().unwrap(),
        Value::String("Bob".to_string()),
    );

    let mut outer_fields = OrderMap::new();
    outer_fields.insert(
        "user".parse::<SimpleIdentifier>().unwrap(),
        Value::Struct(inner_fields),
    );
    outer_fields.insert("score".parse::<SimpleIdentifier>().unwrap(), Value::Int(95));

    assert_eq!(result, Value::Struct(outer_fields));
}

#[test]
fn test_eval_struct_with_variables() {
    // Create context with variables
    let mut ctx = TestContext::default();
    ctx.set("x", Value::Int(42), INT);
    ctx.set("y", Value::String("test".to_string()), STRING);

    // Create struct using variables
    let expr = struct_literal()
        .field("field_x", field_ref("x"))
        .field("field_y", field_ref("y"))
        .field("constant", int(100));

    let result = ctx.eval_expr(&expr);

    let mut expected_fields = OrderMap::new();
    expected_fields.insert(
        "field_x".parse::<SimpleIdentifier>().unwrap(),
        Value::Int(42),
    );
    expected_fields.insert(
        "field_y".parse::<SimpleIdentifier>().unwrap(),
        Value::String("test".to_string()),
    );
    expected_fields.insert(
        "constant".parse::<SimpleIdentifier>().unwrap(),
        Value::Int(100),
    );

    assert_eq!(result, Value::Struct(expected_fields));
}

#[test]
fn test_eval_field_lookup() {
    let ctx = test_context();

    // Helper to create test struct
    let make_struct = || {
        struct_literal()
            .field("name", string("Alice"))
            .field("age", int(30))
            .field("active", boolean(true))
    };

    // Access the 'name' field
    let expr = field(make_struct(), "name");
    let result = ctx.eval_expr(&expr);
    assert_eq!(result, Value::String("Alice".to_string()));

    // Access the 'age' field
    let expr = field(make_struct(), "age");
    let result = ctx.eval_expr(&expr);
    assert_eq!(result, Value::Int(30));

    // Access the 'active' field
    let expr = field(make_struct(), "active");
    let result = ctx.eval_expr(&expr);
    assert_eq!(result, Value::Boolean(true));
}

#[test]
fn test_eval_nested_field_lookup() {
    let ctx = test_context();

    // Create nested struct
    let expr = field(
        field(
            struct_literal().field(
                "user",
                struct_literal().field(
                    "profile",
                    struct_literal()
                        .field("name", string("Bob"))
                        .field("verified", boolean(true)),
                ),
            ),
            "user",
        ),
        "profile",
    );

    let result = ctx.eval_expr(&expr);

    let mut expected_fields = OrderMap::new();
    expected_fields.insert(
        "name".parse::<SimpleIdentifier>().unwrap(),
        Value::String("Bob".to_string()),
    );
    expected_fields.insert(
        "verified".parse::<SimpleIdentifier>().unwrap(),
        Value::Boolean(true),
    );

    assert_eq!(result, Value::Struct(expected_fields));
}

#[test]
fn test_eval_empty_struct() {
    let ctx = test_context();

    // Test empty struct
    let expr = struct_literal();
    let result = ctx.eval_expr(&expr);

    let expected_fields = OrderMap::new();
    assert_eq!(result, Value::Struct(expected_fields));
}

#[test]
fn test_eval_struct_with_null_fields() {
    use hamelin_lib::tree::builder::null;

    let ctx = test_context();

    let expr = struct_literal()
        .field("name", string("Alice"))
        .field("middle_name", null())
        .field("age", int(30));
    let result = ctx.eval_expr(&expr);

    let mut expected_fields = OrderMap::new();
    expected_fields.insert(
        "name".parse::<SimpleIdentifier>().unwrap(),
        Value::String("Alice".to_string()),
    );
    expected_fields.insert(
        "middle_name".parse::<SimpleIdentifier>().unwrap(),
        Value::Null,
    );
    expected_fields.insert("age".parse::<SimpleIdentifier>().unwrap(), Value::Int(30));

    assert_eq!(result, Value::Struct(expected_fields));
}

#[test]
fn test_eval_struct_with_mixed_types() {
    let ctx = test_context();

    let expr = struct_literal()
        .field("string_field", string("text"))
        .field("int_field", int(42))
        .field("bool_field", boolean(true))
        .field("double_field", 3.14);

    let result = ctx.eval_expr(&expr);

    let mut expected_fields = OrderMap::new();
    expected_fields.insert(
        "string_field".parse::<SimpleIdentifier>().unwrap(),
        Value::String("text".to_string()),
    );
    expected_fields.insert(
        "int_field".parse::<SimpleIdentifier>().unwrap(),
        Value::Int(42),
    );
    expected_fields.insert(
        "bool_field".parse::<SimpleIdentifier>().unwrap(),
        Value::Boolean(true),
    );
    expected_fields.insert(
        "double_field".parse::<SimpleIdentifier>().unwrap(),
        Value::Double(3.14),
    );

    assert_eq!(result, Value::Struct(expected_fields));
}

#[test]
fn test_eval_deeply_nested_struct() {
    let ctx = test_context();

    let expr = struct_literal().field(
        "level1",
        struct_literal().field(
            "level2",
            struct_literal().field(
                "level3",
                struct_literal()
                    .field("value", int(123))
                    .field("name", string("deep")),
            ),
        ),
    );

    let result = ctx.eval_expr(&expr);

    // Build expected nested structure
    let mut level3_fields = OrderMap::new();
    level3_fields.insert(
        "value".parse::<SimpleIdentifier>().unwrap(),
        Value::Int(123),
    );
    level3_fields.insert(
        "name".parse::<SimpleIdentifier>().unwrap(),
        Value::String("deep".to_string()),
    );

    let mut level2_fields = OrderMap::new();
    level2_fields.insert(
        "level3".parse::<SimpleIdentifier>().unwrap(),
        Value::Struct(level3_fields),
    );

    let mut level1_fields = OrderMap::new();
    level1_fields.insert(
        "level2".parse::<SimpleIdentifier>().unwrap(),
        Value::Struct(level2_fields),
    );

    let mut expected_fields = OrderMap::new();
    expected_fields.insert(
        "level1".parse::<SimpleIdentifier>().unwrap(),
        Value::Struct(level1_fields),
    );

    assert_eq!(result, Value::Struct(expected_fields));
}

#[test]
fn test_eval_struct_field_access_chain() {
    let ctx = test_context();

    // Create a nested structure and access deeply nested field
    let base_struct = struct_literal().field(
        "user",
        struct_literal().field("id", int(456)).field(
            "profile",
            struct_literal()
                .field("name", string("Charlie"))
                .field("email", string("charlie@example.com")),
        ),
    );

    // Access user.profile.name
    let expr = field(field(field(base_struct, "user"), "profile"), "name");
    let result = ctx.eval_expr(&expr);

    assert_eq!(result, Value::String("Charlie".to_string()));
}

#[test]
fn test_eval_struct_with_array_fields() {
    use hamelin_lib::tree::builder::array;

    let ctx = test_context();

    let expr = struct_literal()
        .field("name", string("Alice"))
        .field("scores", array().element(95).element(87).element(92))
        .field("active", boolean(true));

    let result = ctx.eval_expr(&expr);

    let mut expected_fields = OrderMap::new();
    expected_fields.insert(
        "name".parse::<SimpleIdentifier>().unwrap(),
        Value::String("Alice".to_string()),
    );
    expected_fields.insert(
        "scores".parse::<SimpleIdentifier>().unwrap(),
        Value::Array(vec![Value::Int(95), Value::Int(87), Value::Int(92)]),
    );
    expected_fields.insert(
        "active".parse::<SimpleIdentifier>().unwrap(),
        Value::Boolean(true),
    );

    assert_eq!(result, Value::Struct(expected_fields));
}