- Notifications
You must be signed in to change notification settings - Fork14
Zero-cost high-level lua 5.3 wrapper for Rust
License
Apache-2.0, MIT licenses found
Licenses found
tickbh/td_rlua
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
This library is a high-level binding for Lua 5.3. You don't have access to the Lua stack, all you can do is read/write variables (including callbacks) and execute Lua code.
Add this to theCargo.toml
file of your project
[dependencies]td_rlua ="0.1.0"
externcrate td_rlua;use td_rlua::Lua;
TheLua
struct is the main element of this library. It represents a context in which you can execute Lua code.
letmut 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);
Reading and writing global variables of the Lua context can be done withset
andquery
.Thequery
function returns anOption<T>
and does a copy of the value.
The base types that can be read and written are:i8
,i16
,i32
,u8
,u16
,u32
,f32
,f64
,bool
,String
.&str
can be written but not read.
If you wish so, you can also add other types by implementing theLuaPush
andLuaRead
traits.
let x:u32 = lua.exec_string("return 6 * 2;").unwrap();// equals 12
Theexec_string
function takes a&str
and returns aOption<T>
whereT: LuaRead
.
In order to write a function, you must wrap it aroundtd_rlua::functionX
whereX
is the number of parameters. This is for the moment a limitation of Rust's inferrence system.
fnadd(a:i32,b:i32) ->i32{ a + b}lua.set("add", td_rlua::function2(add));let _:() = lua.exec_string("c = add(2, 4)").unwrap();// calls the `add` function abovelet c:i32 = lua.query("c").unwrap();assert_eq!(c,6);
In Lua, functions are exactly like regular variables.
You can write regular functions as well as closures:
lua.set("mul", td_rlua::function2(|a:i32,b:i32| a* b));
Note that the lifetime of the Lua context must be equal to or shorter than the lifetime of closures. This is enforced at compile-time.
letmut a =5i;{letmut lua =Lua::new(); lua.set("inc", || a +=1);// borrows 'a'for iin(0 ..15){let _:() = lua.exec_string("inc()").unwrap();}}// unborrows `a`assert_eq!(a,20)
extern"C"fnerror_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);
Default in exec_string will call pcall, and set the error_function _G["error_handle"] so you can reset 'error_handle' function to you custom.
Manipulating a Lua table can be done by reading aLuaTable
object. This can be achieved easily by reading aLuaTable
object.
let _:() = lua.exec_string("a = { 9, 8, 7 }").unwrap();letmut 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);
You can then iterate through the table with the.iter()
function. Note that the value returned by the iterator is anOption<(Key, Value)>
, theOption
being empty when either the key or the value is not convertible to the requested type. Thefilter_map
function (provided by the standardIterator
trait) is very useful when dealing with this.
let _:() = lua.exec_string("a = { 9, 8, 7 }").unwrap();letmut table:LuaTable = lua.query("a").unwrap();for _in0 ..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))]);}
When you expose functions to Lua, you may wish to read or write more elaborate objects. This is called auser data.
To do so, you should implement theLuaPush
for your types.This is usually done by redirecting the call touserdata::push_userdata
.it will operate the ref of objectif you useuserdata::push_userdata
the userdata will copy one time, for lua gc managerif you useuserdata::push_lightuserdata
the userdata life manager by rust, so none copy will occup
#[derive(Clone,Debug)]structFoo{a:i32,};impl<'a> td_rlua::LuaPushfor&'amutFoo{fnpush_to_lua(self,lua:*mut c_lua::lua_State) ->i32{ td_rlua::userdata::push_userdata(self, lua, |_|{})}}impl<'a> td_rlua::LuaReadfor&'amutFoo{fnlua_read_at_position(lua:*mut c_lua::lua_State,index:i32) ->Option<&'amutFoo>{ td_rlua::userdata::read_userdata(lua, index)}}let xx =&mutFoo{a:10,};lua.set("a", xx);let get:&mutFoo = lua.query("a").unwrap();assert!(get.a ==10);get.a =100;let get:&mutFoo = lua.query("a").unwrap();assert!(get.a ==100);
use lightuserdata you can change
impl<'a> td_rlua::LuaPushfor&'amutFoo{fnpush_to_lua(self,lua:*mut c_lua::lua_State) ->i32{ td_rlua::userdata::push_lightuserdata(self, lua, |_|{})}}
custom lua call userdata need impl NewStruct
#[derive(Clone,Debug)]structTestLuaSturct{index:i32,}implNewStructforTestLuaSturct{fnnew() ->TestLuaSturct{println!("new !!!!!!!!!!!!!!");TestLuaSturct{index:19,}}fnname() ->&'staticstr{"TestLuaSturct"}}impl<'a>LuaReadfor&'amutTestLuaSturct{fnlua_read_at_position(lua:*mut c_lua::lua_State,index:i32) ->Option<&'amutTestLuaSturct>{ td_rlua::userdata::read_userdata(lua, index)}}
now we can custom function
letmut lua =Lua::new();lua.openlibs();fnone_arg(obj:&mutTestLuaSturct) ->i32{ obj.index =10;5};fntwo_arg(obj:&mutTestLuaSturct,index:i32){ obj.index = index;};letmut value = td_rlua::LuaStruct::<TestLuaSturct>::new(lua.state());value.create().def("one_arg", td_rlua::function1(one_arg)).def("two_arg", td_rlua::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<&mutTestLuaSturct> = 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<&mutTestLuaSturct> = lua.query("x");assert_eq!(obj.unwrap().index,121);let obj:Option<&mutTestLuaSturct> = lua.exec_string("return TestLuaSturct()");assert_eq!(obj.unwrap().index,19);
in runtime, if we need change some logic, we need restart the process, it may lose some memory dataso sometimes we need update the logic, add keep the memory data, so we need hotfix
letmut 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");
the project write refer tohlua, if you use lua5.2, you can use it.
Contributions are welcome!
About
Zero-cost high-level lua 5.3 wrapper for Rust
Topics
Resources
License
Apache-2.0, MIT licenses found
Licenses found
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors2
Uh oh!
There was an error while loading.Please reload this page.