luaext/
context.rs

1use lua::{State, Index, ToLua, FromLua, Function, REGISTRYINDEX};
2use types;
3use std::ptr;
4use error;
5
6/// A wrapper around a Lua State.
7///
8/// Contains its own section of a Lua Stack; when the context goes out of scope, any value pushed
9/// using this context is popped.
10pub struct Context<'a> {
11    state: &'a mut State,
12    target_pos: Index,
13}
14
15/// __gc metamethod used to clean up Rust types that implements the Drop trait.
16pub fn userdata_drop<T>(state: &mut State) -> i32 {
17    use std::ptr;
18    let mut context = Context::new(state);
19    unsafe {
20        // should be safe as long as types match
21        let userdata = context.get_arg_typed::<types::LuaUserdata>(1).unwrap();
22        let entity = userdata.get_value::<T>(&mut context).unwrap();
23        ptr::drop_in_place(entity as *mut T);
24    };
25    0
26}
27
28impl<'a> Context<'a> {
29    /// Creates a new Context using an existing state.
30    pub fn new(state: &mut State) -> Context {
31        let pos = state.get_top();
32        Context {
33            state: state,
34            target_pos: pos,
35        }
36    }
37
38    /// Get this context's contained state.
39    pub fn get_state(&mut self) -> &mut State {
40        self.state
41    }
42
43    /// Push a floating point number onto the stack.
44    pub fn push_number(&mut self, value: f64) -> types::LuaNumber {
45        self.state.push_number(value);
46        let i = self.state.get_top();
47        types::LuaNumber::new(i)
48    }
49
50    /// Push a string onto the stack.
51    pub fn push_string(&mut self, value: &str) -> types::LuaString {
52        self.state.push_string(value);
53        let i = self.state.get_top();
54        types::LuaString::new(i)
55    }
56
57    /// Create a new table and push it into the stack.
58    pub fn push_table(&mut self) -> types::LuaTable {
59        self.state.new_table();
60        let i = self.state.get_top();
61        types::LuaTable::new(i)
62    }
63
64    /// Push a boolean value onto the stack.
65    pub fn push_bool(&mut self, value: bool) -> types::LuaBool {
66        self.state.push_bool(value);
67        let i = self.state.get_top();
68        types::LuaBool::new(i)
69    }
70
71    /// Push a C function onto the stack.
72    pub fn push_function(&mut self, func: Function) -> types::LuaFunction {
73        self.state.push_fn(func);
74        let i = self.state.get_top();
75        types::LuaFunction::new(i)
76    }
77
78    /// Push an integer onto the stack.
79    pub fn push_integer(&mut self, value: i64) -> types::LuaInteger {
80        self.state.push_integer(value);
81        let i = self.state.get_top();
82        types::LuaInteger::new(i)
83    }
84
85    /// Push a nil value onto the stack.
86    pub fn push_nil(&mut self) -> types::LuaNil {
87        self.state.push_nil();
88        let i = self.state.get_top();
89        types::LuaNil::new(i)
90    }
91
92    /// Push a user-defined value onto the stack.
93    pub fn push_userdata<T>(&mut self, value: T) -> types::LuaUserdata {
94        unsafe { ptr::write(self.state.new_userdata_typed(), value); }
95        let i = self.state.get_top();
96        types::LuaUserdata::new(i)
97    }
98
99    /// Push a user-defined value onto the stack, and give it the metatable named 'name.'
100    pub fn push_userdata_named<T>(&mut self, value: T, name: &str) -> types::LuaUserdata {
101        let entity_object = self.push_userdata(value);
102        let entity_object_meta = self.metatable_get(&name).unwrap();
103        entity_object.set_metatable(self, &entity_object_meta);
104        entity_object
105    }
106
107    /// Push a Lua thread onto the stack
108    ///
109    /// You will need to do stuff with the Lua thread in a separate thread if you want two Lua
110    /// threads to run concurrently, Lua doesn't do any actual multithreading itself.
111    pub fn push_thread(&mut self) -> types::LuaThread {
112        self.state.new_thread();
113        let i = self.state.get_top();
114        types::LuaThread::new(i)
115    }
116
117    /// Create a library using an array of Functions, and push the library table onto the stack.,
118    pub fn create_lib(&mut self, lib: &[(&str, Function)]) -> types::LuaTable {
119        self.state.new_lib(lib);
120        let i = self.state.get_top();
121        types::LuaTable::new(i)
122    }
123
124    /// Push a global value onto the stack.
125    pub fn push_global(&mut self, key: &str) -> types::LuaGeneric {
126        self.state.get_global(key);
127        let i = self.state.get_top();
128        types::LuaGeneric::new(i)
129    }
130
131    /// Set a value in the global Lua namespace.
132    pub fn set_global(&mut self, key: &str, value: &ToLua) {
133        value.to_lua(self.state);
134        self.state.set_global(key);
135    }
136
137    /// Get a value from the Lua registry.
138    pub fn get_from_registry(&mut self, key: &ToLua) -> types::LuaGeneric {
139        key.to_lua(self.state);
140        self.state.get_table(REGISTRYINDEX);
141        let i = self.state.get_top();
142        types::LuaGeneric::new(i)
143    }
144
145    /// Get a value from the Lua registry using a type.
146    pub fn get_from_registry_typed<T: FromLua>(&mut self, key: &ToLua) -> Option<T> {
147        key.to_lua(self.state);
148        self.state.get_table(REGISTRYINDEX);
149        let i = self.state.get_top();
150        T::from_lua(&mut self.state, i)
151    }
152
153    /// Set a value in the Lua registry.
154    pub fn set_in_registry(&mut self, key: &ToLua, value: &ToLua) {
155        key.to_lua(self.state);
156        value.to_lua(self.state);
157        self.state.set_table(REGISTRYINDEX);
158    }
159
160    /// Get an argument from this context.
161    pub fn get_arg(&mut self, arg: Index) -> Option<types::LuaGeneric> {
162        if self.state.is_none(arg) {
163            None
164        } else {
165            Some(types::LuaGeneric::new(arg))
166        }
167    }
168
169    /// Get an argument from this context.
170    pub fn get_arg_typed<T: FromLua>(&mut self, arg: Index) -> Option<T> {
171        T::from_lua(&mut self.state, arg)
172    }
173
174    /// Get an argument from this context.
175    ///
176    /// If the argument does not exist, return a default value.
177    pub fn get_arg_typed_or<T: FromLua>(&mut self, arg: Index, value: T) -> Option<T> {
178        if self.state.is_none_or_nil(arg) {
179            Some(value)
180        } else {
181            T::from_lua(&mut self.state, arg)
182        }
183    }
184
185    /// Execute valid Lua code.
186    ///
187    /// # Errors
188    /// Returns an error if the string is not valid Lua,
189    /// or a runtime error occurs during execution.
190    pub fn do_string(&mut self, s: &str) -> error::Result<()> {
191        let threadstatus = self.state.do_string(&s);
192        match error::get_status_from_threadstatus(threadstatus) {
193            Ok(_) => {
194                error::new_luaresult_ok(())
195            },
196            Err(status) => {
197                error::new_luaresult_err(status, error::pop_error_from_state(&mut self.state))
198            }
199        }
200    }
201
202    /// Push a new context on top of the current context.
203    ///
204    /// New values can not be pushed onto the old context until the new context goes out of scope;
205    /// however, values pushed by the old context can still be used by the new context.
206    pub fn push_context(&mut self) -> Context {
207        Context::new(&mut self.state)
208    }
209
210    /// Returns a list of values to Lua.
211    pub fn return_context(mut self, args: &[&ToLua]) -> Index {
212        // push elements in reverse order
213        for arg in args.iter().rev() {
214            arg.to_lua(&mut self.state);
215        }
216        for i in 0..args.len() {
217            self.state.replace(i as Index + self.target_pos);
218        }
219        self.target_pos += args.len() as Index;
220        args.len() as Index
221    }
222
223    /// Register a new metatable in the registry.
224    ///
225    /// Returns a tuple; the first value is if this metatable should be initialized, and the
226    /// second value is the metatable itself.
227    pub fn metatable_register(&mut self, name: &str) -> (bool, types::LuaTable) {
228        let ret = self.state.new_metatable(name);
229        let i = self.state.get_top();
230        let table = types::LuaTable::new(i);
231        (ret, table)
232    }
233
234    /// Get a metatable from the registry.
235    pub fn metatable_get(&mut self, name: &str) -> Option<types::LuaTable> {
236        self.state.get_metatable_from_registry(name);
237        match self.state.is_table(-1) {
238            true => {
239                let i = self.state.get_top();
240                let table = types::LuaTable::new(i);
241                Some(table)
242            },
243            false => {
244                self.state.pop(1);
245                None
246            }
247        }
248    }
249
250    /// Register a named metatable into the Lua registry.
251    ///
252    /// Takes a list of member functions, a list of metamethods, and a unique name for this
253    /// metatable.
254    pub fn metatable_register_named<T>(&mut self, lib: &[(&str, Function)], metamethods: &[(&str, Function)], unique_name: &str)
255            -> Option<types::LuaTable> {
256        let entity_library_members = self.create_lib(lib);
257        let (should_set, entity_metatable) = self.metatable_register(unique_name);
258        if should_set {
259            for &(ref name, ref value) in metamethods.iter() {
260                entity_metatable.set_raw(self, name, value);
261            }
262            entity_metatable.set_raw(self, &"__index", &entity_library_members);
263            entity_metatable.set_raw(self, &"__gc", &lua_func!(userdata_drop<T>));
264            // disable getting of metatables
265            entity_metatable.set_raw(self, &"__metatable", &0);
266        }
267        Some(entity_metatable)
268    }
269}
270
271impl<'a> Drop for Context<'a> {
272    fn drop(&mut self) {
273        self.state.set_top(self.target_pos);
274    }
275}