luau 0.732.0

Safe lifetime-bound Rust embedding API for the Luau runtime
use luau::{Error, Function, Lua, Table, Variadic};

#[test]
fn function_call_grows_stack_for_variadic_arguments() -> Result<(), Error> {
    let lua = Lua::new()?;
    let function = lua
        .load("return function(...) return select('#', ...) end")
        .eval::<Function>()?;

    let count: i32 = function.call(Variadic::from_iter(0..254))?;
    assert_eq!(count, 254);

    Ok(())
}

#[test]
fn function_bind() -> Result<(), Error> {
    let lua = Lua::new()?;
    let function = lua
        .load(
            r#"
            return function(...)
                return table.concat({...})
            end
            "#,
        )
        .eval::<Function>()?;

    let function = function.bind("foo")?;
    let function = function.bind("bar")?;
    let function = function.bind(("baz", "baf"))?;
    assert_eq!(function.call::<String>(())?, "foobarbazbaf");
    assert_eq!(function.call::<String>(("hi", "wut"))?, "foobarbazbafhiwut");

    Ok(())
}

#[test]
fn function_bind_rejects_too_many_arguments() -> Result<(), Error> {
    let lua = Lua::new()?;
    let function = lua.load("return function(...) end").eval::<Function>()?;

    function
        .bind(Variadic::from_iter(0..u8::MAX as usize - 1))?
        .call::<()>(())?;
    assert!(matches!(
        function.bind(Variadic::from_iter(0..u8::MAX as usize)),
        Err(Error::BindError)
    ));

    Ok(())
}

#[test]
fn changing_a_function_environment_invalidates_dependent_imports() -> Result<(), Error> {
    let lua = Lua::new()?;
    lua.globals()?.set("x", 1)?;

    let getter = lua
        .load("return function() return x end")
        .eval::<Function<'_>>()?;
    let module = lua.create_table()?;
    let metatable = lua.create_table()?;
    metatable.set("__index", &getter)?;
    module.set_metatable(Some(&metatable))?;
    lua.globals()?.set("module", module)?;
    lua.sandbox(true)?;

    let read = lua.load("return module.value").into_function()?;
    assert_eq!(read.call::<i32>(())?, 1);

    let environment = lua.create_table()?;
    environment.set("x", 2)?;
    environment.set_safe_env(true);
    assert!(getter.set_environment(&environment)?);
    assert_eq!(read.call::<i32>(())?, 2);
    Ok(())
}

#[test]
fn mutable_callbacks_invalidate_imports_resolved_through_them() -> Result<(), Error> {
    let lua = Lua::new()?;
    let module = lua.create_table()?;
    let metatable = lua.create_table()?;
    let mut value = 0;
    metatable.set(
        "__index",
        lua.create_function_mut(luau::callback!(
            move |_lua, _table: Table<'_>, _key: String| {
                value += 1;
                Ok(value)
            }
        ))?,
    )?;
    module.set_metatable(Some(&metatable))?;
    lua.globals()?.set("module", module)?;
    lua.sandbox(true)?;

    let read = lua.load("return module.value").into_function()?;
    assert_eq!(read.call::<i32>(())?, 2);
    Ok(())
}