3 个版本

0.1.2 2024 年 6 月 6 日
0.1.1 2024 年 6 月 3 日
0.1.0 2024 年 5 月 31 日

#243 in Rust 模式

Download history 426/week @ 2024-05-31 51/week @ 2024-06-07 2/week @ 2024-06-14 1/week @ 2024-06-28 4/week @ 2024-07-05

每月 109 次下载

Apache-2.0

4.5MB
122K SLoC

C 103K SLoC // 0.1% comments Lua 13K SLoC // 0.1% comments Rust 5K SLoC // 0.1% comments Batch 1K SLoC

wmlua

这个库是 Lua 5.4/Lua 5.3/Lua 5.2/Lua 5.1/luajit 的高级绑定。您无法访问 Lua 栈,您所能做的就是读写变量(包括回调)并执行 Lua 代码。

Build Status

如何安装它?

将以下内容添加到您项目的 Cargo.toml 文件中

[dependencies]
wmlua = { version = "0.1.0", features = ["lua54"] }

如何使用它?

extern crate wmlua;
use wmlua::Lua;

Lua 结构体是这个库的主要元素。它表示一个可以执行 Lua 代码的上下文。

let mut lua = Lua::new();     // mutable is mandatory

读写变量

lua.set("x", 2);
let _: () = lua.exec_string("x = x + 1").unwrap();
let x: i32 = lua.query("x").unwrap();
assert_eq!(x, 3);

可以使用 setquery 来读写 Lua 上下文的全局变量。函数 query 返回一个 Option<T> 并复制值。

可以读写的基本类型包括:i8i16i32u8u16u32f32f64boolString。可以写入但不能读取 &str

如果您愿意,也可以通过实现 LuaPushLuaRead 特性来添加其他类型。

执行 Lua

let x: u32 = lua.exec_string("return 6 * 2;").unwrap();    // equals 12

exec_string 函数接受一个 &str 并返回一个 Option<T>,其中 T: LuaRead

编写函数

为了编写函数,您必须将其包装在 wmlua::functionX 中,其中 X 是参数的数量。这目前是 Rust 推断系统的限制。

fn add(a: i32, b: i32) -> i32 {
    a + b
}

lua.set("add", wmlua::function2(add));
let _: () = lua.exec_string("c = add(2, 4)").unwrap();   // calls the `add` function above
let c: i32 = lua.query("c").unwrap();
assert_eq!(c, 6);

在 Lua 中,函数与常规变量完全一样。

您可以编写常规函数以及闭包。

lua.set("mul", wmlua::function2(|a: i32, b: i32| a * b));

请注意,Lua 上下文的生存期必须等于或短于闭包的生存期。这将在编译时强制执行。

let mut a = 5i;

{
    let mut lua = Lua::new();

    lua.set("inc", || a += 1);    // borrows 'a'
    for i in (0 .. 15) {
        let _: () = lua.exec_string("inc()").unwrap();
    }
} // unborrows `a`

assert_eq!(a, 20)
错误处理
extern "C" fn error_handle(lua: *mut c_lua::lua_State) -> libc::c_int {
    let err = unsafe { c_lua::lua_tostring(lua, -1) };
    let err = unsafe { CStr::from_ptr(err) };
    let err = String::from_utf8(err.to_bytes().to_vec()).unwrap();
    println!("error:{}", err);
    0
}
lua.register("error_handle", error_handle);

默认情况下,exec_string 会调用 pcall,并将错误函数 _G["error_handle"] 设置为,以便您可以重置 'error_handle' 函数为您自定义。

操作 Lua 表

通过读取一个 LuaTable 对象来操作 Lua 表格。这可以通过读取一个 LuaTable 对象轻松实现。

let _:() = lua.exec_string("a = { 9, 8, 7 }").unwrap();
let mut table : LuaTable = lua.query("a").unwrap();

let x: i32 = table.query(2).unwrap();
assert_eq!(x, 8);

table.set(3, "hello");
let y: String = table.query(3).unwrap();
assert_eq!(y, "hello");

let z: i32 = table.query(1).unwrap();
assert_eq!(z, 9);

然后,您可以使用 .iter() 函数遍历表格。请注意,迭代器返回的值是一个 Option<(Key, Value)>,当键或值无法转换为请求的类型时,该 Option 为空。在这种情况下,标准 Iterator 特性提供的 filter_map 函数非常有用。

let _:() = lua.exec_string("a = { 9, 8, 7 }").unwrap();
let mut table : LuaTable = lua.query("a").unwrap();
for _ in 0 .. 10 {
    let table_content: Vec<Option<(u32, u32)>> = table.iter().collect();
    assert_eq!(table_content, vec![ Some((1,9)), Some((2,8)), Some((3,7)) ]);
}

用户数据

当您将函数暴露给 Lua 时,您可能希望读取或写入更复杂的数据对象。这被称为 用户数据

为此,您应该为您的类型实现 LuaPush。这通常是通过将调用重定向到 userdata::push_userdata 来完成的。如果您使用 userdata::push_userdata,则将对对象的引用进行操作;如果使用 userdata::push_lightuserdata,则由 Rust 管理用户数据的生命周期,因此不会进行复制。

use wmlua::{add_object_field, lua_State, object_impl, LuaObject, LuaPush, LuaRead};

#[derive(Default)]
struct Xx {
    kk: String,
    nn: String,
}

object_impl!(Xx);

fn main() {
    let mut lua = wmlua::Lua::new();
    let mut object = LuaObject::<Xx>::new(lua.state(), "CCCC");
    object.create();
    add_object_field!(object, kk, Xx, String);
    object.add_method_get("xxx", wmlua::function1(|obj: &mut Xx| "sss is xxx".to_string()));
    lua.openlibs();

    let val = "
        print(aaa);
        print(\"cccxxxxxxxxxxxxxxx\");
        print(type(CCCC));
        local v = CCCC();
        print(\"vvvvv\", v:xxx())
        print(\"kkkk\", v.kk)
        v.kk = \"aa\";
        print(\"ccccc\", v.kk)
        print(\"vvvvv\", v:xxx())
    ";

    let _: Option<()> = lua.exec_string(val);
}

现在我们可以自定义函数

let mut lua = Lua::new();
lua.openlibs();
fn one_arg(obj : &mut TestLuaSturct) -> i32 { obj.index = 10; 5 };
fn two_arg(obj : &mut TestLuaSturct, index : i32) { obj.index = index;};

let mut value = wmlua::LuaStruct::<TestLuaSturct>::new(lua.state());
value.create().def("one_arg", wmlua::function1(one_arg)).def("two_arg", wmlua::function2(two_arg));

let _ : Option<()> = lua.exec_string("x = TestLuaSturct()");
let val : Option<i32> = lua.exec_string("return x:one_arg()");
assert_eq!(val, Some(5));
let obj : Option<&mut TestLuaSturct> = lua.query("x");
assert_eq!(obj.unwrap().index, 10);
let val : Option<i32> = lua.exec_string("x:two_arg(121)");
assert_eq!(val, None);
let obj : Option<&mut TestLuaSturct> = lua.query("x");
assert_eq!(obj.unwrap().index, 121);

let obj : Option<&mut TestLuaSturct> = lua.exec_string("return TestLuaSturct()");
assert_eq!(obj.unwrap().index, 19);

热修复

在运行时,如果我们需要更改某些逻辑,我们需要重新启动进程,这可能会丢失一些内存数据,因此有时我们需要更新逻辑,同时保留内存数据,因此我们需要热修复。

let mut lua = Lua::new();
lua.openlibs();
lua.enable_hotfix();
let _ = lua.exec_func2("hotfix", r"
    local value = {3, 4}
    function get_a()
        value[2] = 3
        return value[1]
    end

    function get_b()
        return value[2]
    end
    ", "hotfix");

参考

该项目参考了 td_rlua

贡献

欢迎贡献!

依赖项