#methods #user #rlua #index #data #macro #generate

rudeboy

Rlua 用户数据扩展库 - 用于轻松将用户数据导出到 RLua 的 Derive/attr 宏和特质

2 个不稳定版本

0.2.0 2020 年 7 月 1 日
0.1.0 2020 年 6 月 28 日

#2603Rust 模式

MIT 许可证

21KB

rudeboy

rudeboy - Rlua 用户数据扩展库

提供 derive 宏和 impl 块属性宏,允许通过 rlua 包将 Rust 方法暴露给 Lua,同时生成索引元方法。

用法

该库允许以下五种主要用法,以下各节将进行说明。

仅索引元方法

这允许通过 instance.field 语法从 Lua 访问 UserData 结构体的实例字段,但不生成或允许用户添加任何其他方法。

use rudeboy::IndexSealed;

#[derive(IndexSealed)]
struct Foo {
    bar: String,
    baz: f64,
}

let lua = rlua::Lua::new();
lua.context(|ctx| {
    // Add an instance of Foo to the lua environment
    let globals = ctx.globals();
    let bar = "bar".to_string();
    let baz = 23.0;
    globals.set("a_foo", Foo { bar: bar.clone(), baz })?;

    // Use the index metamethod to access fields
    let lua_bar = ctx.load("a_foo.bar").eval::<String>()?;
    assert_eq!(lua_bar, bar);
    let lua_baz = ctx.load("a_foo.baz").eval::<f64>()?;
    assert_eq!(lua_baz, baz);

    Ok(())
})?;

索引元方法和额外的用户定义

这允许通过 instance.field 语法从 Lua 访问 UserData 结构体的实例字段,但不生成 rlua::UserData 的 impl。用户必须使用 RudeboyIndex 特质来添加索引元方法,但也可以自由添加从 Lua 访问的其他方法。

use rudeboy::Index;

#[derive(Index)]
struct Foo {
    bar: String,
    baz: f64,
}

impl rlua::UserData for Foo {
    fn add_methods<'lua, M: ::rlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
        // Use the rudeboy-generated trait to add the index metamethod
        use rudeboy::RudeboyIndex;
        Foo::generate_index(methods);

        // Add additional user-defined methods
        methods.add_method("user_method", |_, data, ()| {
            Ok(data.baz * 2.0)
        });
    }
}

let lua = rlua::Lua::new();
lua.context(|ctx| {
    // Add an instance of Foo to the lua environment
    let globals = ctx.globals();
    let bar = "bar".to_string();
    let baz = 23.0;
    globals.set("a_foo", Foo { bar: bar.clone(), baz })?;

    // Use the index metamethod to access fields
    let lua_bar = ctx.load("a_foo.bar").eval::<String>()?;
    assert_eq!(lua_bar, bar);
    let lua_baz = ctx.load("a_foo.baz").eval::<f64>()?;
    assert_eq!(lua_baz, baz);

    // Use the user defined method
    let udm = ctx.load("a_foo:user_method()").eval::<f64>()?;
    assert_eq!(baz * 2.0, udm);

    Ok(())
})?;

仅索引元方法和 Rust 方法

这生成索引元方法,并将标记 impl 块中的方法暴露给 Lua,同时生成 UserData 的 impl。然而,用户不能添加任何进一步的用户定义方法。

use rudeboy::{Index, MethodsSealed};

#[derive(Index)]
struct Foo {
    bar: String,
    baz: f64,
}

#[MethodsSealed]
impl Foo {
    // Methods must take self as their receiver...
    fn double(&self) -> f64 {
        self.baz * 2.0
    }

    // ... but can take mut self as well
    fn set_baz(&mut self, baz: f64) {
        self.baz = baz;
    }
}

let lua = rlua::Lua::new();
lua.context(|ctx| {
    // Add an instance of Foo to the lua environment
    let globals = ctx.globals();
    let bar = "bar".to_string();
    let baz = 23.0;
    globals.set("a_foo", Foo { bar: bar.clone(), baz })?;

    // Use the index metamethod to access fields
    let lua_bar = ctx.load("a_foo.bar").eval::<String>()?;
    assert_eq!(lua_bar, bar);
    let lua_baz = ctx.load("a_foo.baz").eval::<f64>()?;
    assert_eq!(lua_baz, baz);

    // Use the immutable method
    let doubled = ctx.load("a_foo:double()").eval::<f64>()?;
    assert_eq!(baz * 2.0, doubled);

    // Use the mutable method
    ctx.load("a_foo:set_baz(5.0)").exec()?;
    let new_baz = ctx.load("a_foo.baz").eval::<f64>()?;
    assert_eq!(new_baz, 5.0);

    Ok(())
})?;

索引元方法和 Rust 方法以及额外的用户定义

这生成索引元方法,并将标记 impl 块中的方法暴露给 Lua,但不生成 UserData 的 impl。然后,用户可以使用 rlua::UserData 添加额外的方法,但必须使用 RudeboyIndexRudeboyMethods 特质将生成的方法添加到用户数据中。

use rudeboy::{Index, Methods};

#[derive(Index)]
struct Foo {
    bar: String,
    baz: f64,
}

#[Methods]
impl Foo {
    // Methods must take self as their receiver...
    fn double(&self) -> f64 {
        self.baz * 2.0
    }

    // ... but can take mut self as well
    fn set_baz(&mut self, baz: f64) {
        self.baz = baz;
    }
}

impl rlua::UserData for Foo {
    fn add_methods<'lua, M: ::rlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
        // Use the rudeboy-generated trait to add the index metamethod
        use rudeboy::RudeboyIndex;
        Foo::generate_index(methods);

        // Use the rudeboy-generated trait to add the methods from the tagged
        // impl block
        use rudeboy::RudeboyMethods;
        Foo::generate_methods(methods);

        // Add additional user-defined methods
        methods.add_method("user_method", |_, data, ()| {
            Ok(data.baz * 2.0)
        });
    }
}

let lua = rlua::Lua::new();
lua.context(|ctx| {
    // Add an instance of Foo to the lua environment
    let globals = ctx.globals();
    let bar = "bar".to_string();
    let baz = 23.0;
    globals.set("a_foo", Foo { bar: bar.clone(), baz })?;

    // Use the index metamethod to access fields
    let lua_bar = ctx.load("a_foo.bar").eval::<String>()?;
    assert_eq!(lua_bar, bar);
    let lua_baz = ctx.load("a_foo.baz").eval::<f64>()?;
    assert_eq!(lua_baz, baz);

    // Use the immutable method
    let udm = ctx.load("a_foo:double()").eval::<f64>()?;
    assert_eq!(baz * 2.0, udm);

    // Use the mutable method
    ctx.load("a_foo:set_baz(5.0)").exec()?;
    let new_baz = ctx.load("a_foo.baz").eval::<f64>()?;
    assert_eq!(new_baz, 5.0);

    // Use the user defined method
    let udm = ctx.load("a_foo:user_method()").eval::<f64>()?;
    assert_eq!(new_baz * 2.0, udm);

    Ok(())
})?;

无索引元方法的 Rust 方法

在 impl 块中暴露方法到 Lua,而不为类型创建索引元方法。同时生成 rlua::UserData 的 impl,这意味着用户不能添加额外的用户定义方法。

本例使用MethodsSealed将impl块中的方法添加进来,并为该类型生成一个rlua::UserData的impl实现。请注意,MethodsSealed期望类型有一个RudeboyIndex的实现,因此我们必须使用NoIndex宏来提供一个空的实现。

use rudeboy::{NoIndex, MethodsSealed};

// Derives Clone so that an instance can be retrieved from the lua context
#[derive(Clone, NoIndex)]
struct Foo {
    bar: String,
    baz: f64,
}

#[MethodsSealed]
impl Foo {
    // Methods must take self as their receiver...
    fn double(&self) -> f64 {
        self.baz * 2.0
    }

    // ... but can take mut self as well
    fn set_baz(&mut self, baz: f64) {
        self.baz = baz;
    }
}

let lua = rlua::Lua::new();
lua.context(|ctx| {
    // Add an instance of Foo to the lua environment
    let globals = ctx.globals();
    let bar = "bar".to_string();
    let baz = 23.0;
    globals.set("a_foo", Foo { bar: bar.clone(), baz })?;

    // Use the immutable method
    let udm = ctx.load("a_foo:double()").eval::<f64>()?;
    assert_eq!(baz * 2.0, udm);

    // Use the mutable method
    ctx.load("a_foo:set_baz(5.0)").exec()?;
    let new_foo = ctx.load("a_foo").eval::<Foo>()?;
    assert_eq!(new_foo.baz, 5.0);

    Ok(())
})?;

没有索引元方法的Rust方法,但有用户定义的方法

这会生成一个RudeboyMethods的实现,该实现将添加来自impl块的方法,但不会生成索引元方法或为rlua::UserData生成impl。这允许用户添加额外的用户定义方法。

请注意,因为这种方法没有使用MethodsSealed,所以不需要使用NoIndex宏。

use rudeboy::{Index, Methods};

// Derives Clone so that an instance can be retrieved from the lua context
#[derive(Clone)]
struct Foo {
    bar: String,
    baz: f64,
}

#[Methods]
impl Foo {
    // Methods must take self as their receiver...
    fn double(&self) -> f64 {
        self.baz * 2.0
    }

    // ... but can take mut self as well
    fn set_baz(&mut self, baz: f64) {
        self.baz = baz;
    }
}

impl rlua::UserData for Foo {
    fn add_methods<'lua, M: ::rlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
        // Note: the call for RudeboyIndex::generate_index is simply ommitted

        // Use the rudeboy-generated trait to add the methods from the tagged
        // impl block
        use rudeboy::RudeboyMethods;
        Foo::generate_methods(methods);

        // Add additional user-defined methods
        methods.add_method("user_method", |_, data, ()| {
            Ok(data.baz * 2.0)
        });
    }
}

let lua = rlua::Lua::new();
lua.context(|ctx| {
    // Add an instance of Foo to the lua environment
    let globals = ctx.globals();
    let bar = "bar".to_string();
    let baz = 23.0;
    globals.set("a_foo", Foo { bar: bar.clone(), baz })?;

    // Use the immutable method
    let udm = ctx.load("a_foo:double()").eval::<f64>()?;
    assert_eq!(baz * 2.0, udm);

    // Use the mutable method
    ctx.load("a_foo:set_baz(5.0)").exec()?;
    let new_foo = ctx.load("a_foo").eval::<Foo>()?;
    assert_eq!(new_foo.baz, 5.0);

    // Use the user defined method
    let udm = ctx.load("a_foo:user_method()").eval::<f64>()?;
    assert_eq!(new_foo.baz * 2.0, udm);

    Ok(())
})?;

许可证:MIT

依赖关系

~2.5MB
~51K SLoC