pm_rlua/
lib.rs

1#![allow(clippy::too_many_arguments)]
2extern crate libc;
3extern crate td_clua;
4
5use std::borrow::Borrow;
6use std::ffi::{CStr, CString};
7use std::fs::File;
8use std::io::prelude::*;
9
10macro_rules! unwrap_or {
11    ($expr:expr, $or:expr) => {
12        match $expr {
13            Some(x) => x,
14            None => $or,
15        }
16    };
17}
18
19pub mod functions;
20mod hotfix;
21pub mod lua_tables;
22pub mod rust_tables;
23pub mod tuples;
24pub mod userdata;
25pub mod values;
26
27pub use functions::{
28    function0, function1, function10, function2, function3, function4, function5, function6,
29    function7, function8, function9, Function,
30};
31pub use lua_tables::LuaTable;
32pub use td_clua::*;
33pub use userdata::{push_lightuserdata, push_userdata, read_userdata, LuaStruct, NewStruct};
34pub struct Lua {
35    lua: *mut lua_State,
36    own: bool,
37}
38
39pub struct LuaGuard {
40    pub lua: *mut lua_State,
41    pub size: i32,
42}
43
44impl LuaGuard {
45    pub fn forget(mut self) -> i32 {
46        let size = self.size;
47        self.size = 0;
48        size
49    }
50
51    pub fn empty(&self) -> LuaGuard {
52        LuaGuard {
53            lua: self.lua,
54            size: 0,
55        }
56    }
57
58    pub fn new_empty(lua: *mut lua_State) -> LuaGuard {
59        LuaGuard { lua: lua, size: 0 }
60    }
61
62    pub fn new(lua: *mut lua_State, size: i32) -> LuaGuard {
63        LuaGuard {
64            lua: lua,
65            size: size,
66        }
67    }
68}
69
70macro_rules! impl_exec_func {
71    ($name:ident, $($p:ident),*) => (
72        #[allow(non_snake_case, unused_mut)]
73        pub fn $name<Z, $($p),*>(&mut self, func_name : Z, $($p : $p, )*) -> i32 where Z: Borrow<str>, $($p : LuaPush),* {
74            let func_name = CString::new(func_name.borrow()).unwrap();
75            unsafe {
76                let state = self.state();
77                let error = CString::new("error_handle").unwrap();
78                lua_getglobal(state, error.as_ptr());
79                td_clua::lua_getglobal(state, func_name.as_ptr());
80
81                let mut index = 0;
82                $(
83                    index += $p.push_to_lua(self.state());
84                )*
85
86                let success = td_clua::lua_pcall(state, index, 0, -index - 2);
87                if success != 0 {
88                    td_clua::lua_pop(state, 1);
89                }
90                td_clua::lua_pop(state, 1);
91                success
92            }
93        }
94    )
95}
96
97impl Default for Lua {
98    fn default() -> Self {
99        Self::new()
100    }
101}
102
103impl Lua {
104    /// Builds a new Lua context.
105    ///
106    /// # Panic
107    ///
108    /// The function panics if the underlying call to `luaL_newstate` fails
109    /// (which indicates lack of memory).
110    pub fn new() -> Lua {
111        let lua = unsafe { td_clua::luaL_newstate() };
112        if lua.is_null() {
113            panic!("lua_newstate failed");
114        }
115
116        // called whenever lua encounters an unexpected error.
117        extern "C" fn panic(lua: *mut td_clua::lua_State) -> libc::c_int {
118            let err = unsafe { td_clua::lua_tostring(lua, -1) };
119            let err = unsafe { CStr::from_ptr(err) };
120            let err = String::from_utf8(err.to_bytes().to_vec()).unwrap();
121            panic!("PANIC: unprotected error in call to Lua API ({})\n", err);
122        }
123
124        extern "C" fn error_handle(lua: *mut td_clua::lua_State) -> libc::c_int {
125            let err = unsafe { td_clua::lua_tostring(lua, -1) };
126            let err = unsafe { CStr::from_ptr(err) };
127            let err = String::from_utf8(err.to_bytes().to_vec()).unwrap();
128            println!("error:{}", err);
129            0
130        }
131
132        unsafe { td_clua::lua_atpanic(lua, panic) };
133        let mut lua = Lua {
134            lua: lua,
135            own: true,
136        };
137        lua.register("error_handle", error_handle);
138        lua
139    }
140
141    pub fn state(&mut self) -> *mut lua_State {
142        self.lua
143    }
144
145    pub fn clone(&mut self) -> Lua {
146        Lua {
147            lua: self.lua,
148            own: false,
149        }
150    }
151
152    pub fn set_own(&mut self, own: bool) {
153        self.own = own;
154    }
155
156    /// Takes an existing `lua_State` and build a Lua object from it.
157    ///
158    /// # Arguments
159    ///
160    ///  * `close_at_the_end`: if true, lua_close will be called on the lua_State on the destructor
161    pub fn from_existing_state(lua: *mut lua_State, close_at_the_end: bool) -> Lua {
162        Lua {
163            lua: lua,
164            own: close_at_the_end,
165        }
166    }
167
168    pub fn register<I>(
169        &mut self,
170        index: I,
171        func: extern "C" fn(*mut td_clua::lua_State) -> libc::c_int,
172    ) -> i32
173    where
174        I: Borrow<str>,
175    {
176        let index = CString::new(index.borrow()).unwrap();
177        unsafe { td_clua::lua_register(self.state(), index.as_ptr(), func) };
178        0
179    }
180
181    /// Opens all standard Lua libraries.
182    /// This is done by calling `luaL_openlibs`.
183    pub fn openlibs(&mut self) {
184        unsafe { td_clua::luaL_openlibs(self.lua) }
185    }
186
187    /// Reads the value of a global variable.
188    pub fn query<V, I>(&mut self, index: I) -> Option<V>
189    where
190        I: Borrow<str>,
191        V: LuaRead,
192    {
193        let index = CString::new(index.borrow()).unwrap();
194        unsafe {
195            td_clua::lua_getglobal(self.lua, index.as_ptr());
196        }
197        let _guard = LuaGuard::new(self.lua, 1);
198        LuaRead::lua_read_with_pop(self.state(), -1, 1)
199    }
200
201    /// Modifies the value of a global variable.
202    pub fn set<I, V>(&mut self, index: I, value: V)
203    where
204        I: Borrow<str>,
205        for<'a> V: LuaPush,
206    {
207        let index = CString::new(index.borrow()).unwrap();
208        value.push_to_lua(self.state());
209        unsafe {
210            td_clua::lua_setglobal(self.lua, index.as_ptr());
211        }
212    }
213
214    pub fn exec_string<I, R>(&mut self, index: I) -> Option<R>
215    where
216        I: Borrow<str>,
217        R: LuaRead,
218    {
219        let index = CString::new(index.borrow()).unwrap();
220        unsafe {
221            let state = self.state();
222            let error = CString::new("error_handle").unwrap();
223            td_clua::lua_getglobal(state, error.as_ptr());
224            td_clua::luaL_loadstring(state, index.as_ptr());
225            let success = td_clua::lua_pcall(state, 0, 1, -2);
226            if success != 0 {
227                td_clua::lua_pop(state, 1);
228                return None;
229            }
230            LuaRead::lua_read(state)
231        }
232    }
233
234    pub fn exec_func<I, R>(&mut self, index: I) -> Option<R>
235    where
236        I: Borrow<str>,
237        R: LuaRead,
238    {
239        let index = CString::new(index.borrow()).unwrap();
240        unsafe {
241            let state = self.state();
242            let error = CString::new("error_handle").unwrap();
243            let top = td_clua::lua_gettop(state);
244            td_clua::lua_getglobal(state, index.as_ptr());
245            td_clua::lua_insert(state, -top - 1);
246            td_clua::lua_getglobal(state, error.as_ptr());
247            td_clua::lua_insert(state, -top - 2);
248            let success = td_clua::lua_pcall(state, top, 1, -top - 2);
249            if success != 0 {
250                td_clua::lua_pop(state, 1);
251                return None;
252            }
253            LuaRead::lua_read(state)
254        }
255    }
256
257    /// Inserts an empty table, then loads it.
258    pub fn empty_table<I>(&mut self, index: I) -> LuaTable
259    where
260        I: Borrow<str>,
261    {
262        let index2 = CString::new(index.borrow()).unwrap();
263        unsafe {
264            td_clua::lua_newtable(self.state());
265            td_clua::lua_setglobal(self.state(), index2.as_ptr());
266        }
267        self.query(index).unwrap()
268    }
269
270    pub fn add_lualoader(
271        &mut self,
272        func: extern "C" fn(*mut td_clua::lua_State) -> libc::c_int,
273    ) -> i32 {
274        let state = self.state();
275        unsafe {
276            let package = CString::new("package").unwrap();
277            let searchers = CString::new("searchers").unwrap();
278            td_clua::lua_getglobal(state, package.as_ptr());
279            td_clua::lua_getfield(state, -1, searchers.as_ptr());
280            td_clua::lua_pushcfunction(state, func);
281            let mut i = (td_clua::lua_rawlen(state, -2) + 1) as i32;
282            while i > 2 {
283                td_clua::lua_rawgeti(state, -2, i - 1);
284                td_clua::lua_rawseti(state, -3, i);
285                i -= 1;
286            }
287            td_clua::lua_rawseti(state, -2, 2);
288            // set loaders into package
289            td_clua::lua_setfield(state, -2, searchers.as_ptr());
290            td_clua::lua_pop(state, 1);
291        }
292        0
293    }
294
295    pub fn load_file(&mut self, file_name: &str) -> i32 {
296        let mut f = unwrap_or!(File::open(file_name).ok(), return 0);
297        let mut buffer = Vec::new();
298        let _ = unwrap_or!(f.read_to_end(&mut buffer).ok(), return 0);
299        let mut name = file_name.to_string();
300        let mut short_name = name.clone();
301        let len = name.len();
302        if len > 30 {
303            short_name = name.drain((len - 30)..).collect();
304        }
305
306        let short_name = CString::new(short_name).unwrap();
307        let ret = unsafe {
308            td_clua::luaL_loadbuffer(
309                self.state(),
310                buffer.as_ptr() as *const libc::c_char,
311                buffer.len(),
312                short_name.as_ptr(),
313            )
314        };
315        if ret != 0 {
316            let err_msg: String = unwrap_or!(LuaRead::lua_read(self.state()), return 0);
317            let err_detail = CString::new(format!(
318                "error loading from file {} :\n\t{}",
319                file_name, err_msg
320            ))
321            .unwrap();
322            unsafe {
323                td_clua::luaL_error(self.state(), err_detail.as_ptr());
324            }
325        }
326        1
327    }
328
329    /// enable hotfix, can update the new func, and the old data will be keep and bind to the new func
330    pub fn enable_hotfix(&mut self) {
331        hotfix::load_hot_fix(self);
332    }
333
334    pub fn exec_gc(&mut self) -> i32 {
335        unsafe { td_clua::lua_gc(self.state(), td_clua::LUA_GCCOLLECT, 0) as i32 }
336    }
337
338    impl_exec_func!(exec_func0,);
339    impl_exec_func!(exec_func1, A);
340    impl_exec_func!(exec_func2, A, B);
341    impl_exec_func!(exec_func3, A, B, C);
342    impl_exec_func!(exec_func4, A, B, C, D);
343    impl_exec_func!(exec_func5, A, B, C, D, E);
344    impl_exec_func!(exec_func6, A, B, C, D, E, F);
345    impl_exec_func!(exec_func7, A, B, C, D, E, F, G);
346    impl_exec_func!(exec_func8, A, B, C, D, E, F, G, H);
347    impl_exec_func!(exec_func9, A, B, C, D, E, F, G, H, I);
348    impl_exec_func!(exec_func10, A, B, C, D, E, F, G, H, I, J);
349}
350
351/// Types that can be given to a Lua context, for example with `lua.set()` or as a return value
352/// of a function.
353pub trait LuaPush {
354    /// Pushes the value on the top of the stack.
355    ///
356    /// Must return a guard representing the elements that have been pushed.
357    ///
358    /// You can implement this for any type you want by redirecting to call to
359    /// another implementation (for example `5.push_to_lua`) or by calling
360    /// `userdata::push_userdata`.
361    fn push_to_lua(self, lua: *mut lua_State) -> i32;
362}
363
364/// Types that can be obtained from a Lua context.
365///
366/// Most types that implement `LuaPush` also implement `LuaRead`, but this is not always the case
367/// (for example `&'static str` implements `LuaPush` but not `LuaRead`).
368pub trait LuaRead: Sized {
369    /// Reads the data from Lua.
370    fn lua_read(lua: *mut lua_State) -> Option<Self> {
371        LuaRead::lua_read_at_position(lua, -1)
372    }
373
374    /// Reads the data from Lua at a given position.
375    fn lua_read_at_position(lua: *mut lua_State, index: i32) -> Option<Self> {
376        LuaRead::lua_read_with_pop(lua, index, 0)
377    }
378
379    /// Reads the data from Lua at a given position.
380    fn lua_read_with_pop(lua: *mut lua_State, index: i32, pop: i32) -> Option<Self>;
381}
382
383impl Drop for Lua {
384    fn drop(&mut self) {
385        if self.own {
386            unsafe { td_clua::lua_close(self.lua) }
387        }
388    }
389}
390
391impl Drop for LuaGuard {
392    fn drop(&mut self) {
393        if self.size != 0 {
394            unsafe { td_clua::lua_pop(self.lua, self.size) }
395        }
396    }
397}