1use std::any::{Any, TypeId};
2use std::boxed::Box;
3use std::ffi::CString;
4use std::marker::PhantomData;
5use std::mem;
6use std::ptr;
7
8use libc;
9use td_clua;
10use td_clua::lua_State;
11use Lua;
12use LuaPush;
13use LuaRead;
14use LuaTable;
15
16#[inline]
18extern "C" fn destructor_wrapper<T>(lua: *mut td_clua::lua_State) -> libc::c_int {
19 unsafe {
20 let obj = td_clua::lua_touserdata(lua, -1);
21 ptr::drop_in_place(obj as *mut T);
22 0
23 }
24}
25
26extern "C" fn constructor_wrapper<T>(lua: *mut td_clua::lua_State) -> libc::c_int
27where
28 T: NewStruct + Any,
29{
30 let t = T::new();
31 let lua_data_raw =
32 unsafe { td_clua::lua_newuserdata(lua, mem::size_of::<T>() as libc::size_t) };
33 unsafe {
34 ptr::write(lua_data_raw as *mut _, t);
35 }
36 let typeid = CString::new(T::name()).unwrap();
37 unsafe {
38 td_clua::lua_getglobal(lua, typeid.as_ptr());
39 td_clua::lua_setmetatable(lua, -2);
40 }
41 1
42}
43
44extern "C" fn constructor_light_wrapper<T>(lua: *mut td_clua::lua_State) -> libc::c_int
48where
49 T: NewStruct + Any,
50{
51 let t = Box::into_raw(Box::new(T::new()));
52 push_lightuserdata(unsafe { &mut *t }, lua, |_| {});
53 let typeid = CString::new(T::name()).unwrap();
54 unsafe {
55 td_clua::lua_getglobal(lua, typeid.as_ptr());
56 td_clua::lua_setmetatable(lua, -2);
57 }
58 1
59}
60
61pub fn push_userdata<'a, T, F>(data: T, lua: *mut td_clua::lua_State, mut metatable: F) -> i32
74where
75 F: FnMut(LuaTable),
76 T: 'a + Any,
77{
78 let typeid = format!("{:?}", TypeId::of::<T>());
79 let lua_data_raw =
80 unsafe { td_clua::lua_newuserdata(lua, mem::size_of::<T>() as libc::size_t) };
81
82 unsafe {
84 ptr::write(lua_data_raw as *mut _, data);
85
86 td_clua::lua_newtable(lua);
87
88 "__typeid".push_to_lua(lua);
90 typeid.push_to_lua(lua);
91 td_clua::lua_settable(lua, -3);
92
93 {
95 "__gc".push_to_lua(lua);
96
97 td_clua::lua_pushcfunction(lua, destructor_wrapper::<T>);
98
99 td_clua::lua_settable(lua, -3);
100 }
101
102 {
104 metatable(LuaRead::lua_read(lua).unwrap());
105 }
106
107 td_clua::lua_setmetatable(lua, -2);
108 }
109
110 1
111}
112
113pub fn push_lightuserdata<'a, T, F>(
126 data: &'a mut T,
127 lua: *mut td_clua::lua_State,
128 mut metatable: F,
129) -> i32
130where
131 F: FnMut(LuaTable),
132 T: 'a + Any,
133{
134 let typeid = format!("{:?}", TypeId::of::<T>());
135 unsafe {
136 td_clua::lua_pushlightuserdata(lua, mem::transmute(data));
137 };
138
139 unsafe {
141 td_clua::lua_newtable(lua);
142
143 "__typeid".push_to_lua(lua);
145 typeid.push_to_lua(lua);
146 td_clua::lua_settable(lua, -3);
147
148 {
150 metatable(LuaRead::lua_read(lua).unwrap());
151 }
152
153 td_clua::lua_setmetatable(lua, -2);
154 }
155
156 1
157}
158
159pub fn read_userdata<'t, T>(lua: *mut td_clua::lua_State, index: i32) -> Option<&'t mut T>
161where
162 T: 'static + Any,
163{
164 unsafe {
165 let expected_typeid = format!("{:?}", TypeId::of::<T>());
166 let data_ptr = td_clua::lua_touserdata(lua, index);
167 if data_ptr.is_null() {
168 return None;
169 }
170 if td_clua::lua_getmetatable(lua, index) == 0 {
171 return None;
172 }
173
174 "__typeid".push_to_lua(lua);
175 td_clua::lua_gettable(lua, -2);
176 match <String as LuaRead>::lua_read(lua) {
177 Some(ref val) if val == &expected_typeid => {}
178 _ => {
179 return None;
180 }
181 }
182 td_clua::lua_pop(lua, 2);
183 Some(&mut *(data_ptr as *mut T))
184 }
185}
186
187pub trait NewStruct {
188 fn new() -> Self;
189 fn name() -> &'static str;
190}
191
192pub struct LuaStruct<T> {
193 lua: *mut lua_State,
194 light: bool,
195 marker: PhantomData<T>,
196}
197
198impl<T> LuaStruct<T>
199where
200 T: NewStruct + Any,
201{
202 pub fn new(lua: *mut lua_State) -> LuaStruct<T> {
203 LuaStruct {
204 lua: lua,
205 light: false,
206 marker: PhantomData,
207 }
208 }
209
210 pub fn new_light(lua: *mut lua_State) -> LuaStruct<T> {
211 LuaStruct {
212 lua: lua,
213 light: true,
214 marker: PhantomData,
215 }
216 }
217
218 pub fn ensure_matetable(&mut self) {
219 let name = T::name();
220 let mut lua = Lua::from_existing_state(self.lua, false);
221
222 match lua.query::<LuaTable, _>(name.clone()) {
223 Some(_) => {}
224 None => unsafe {
225 td_clua::lua_newtable(self.lua);
226
227 let typeid = format!("{:?}", TypeId::of::<T>());
228 "__typeid".push_to_lua(self.lua);
230 typeid.push_to_lua(self.lua);
231 td_clua::lua_settable(self.lua, -3);
232
233 if !self.light {
235 "__gc".push_to_lua(self.lua);
236
237 td_clua::lua_pushcfunction(self.lua, destructor_wrapper::<T>);
238
239 td_clua::lua_settable(self.lua, -3);
240 }
241
242 "__index".push_to_lua(self.lua);
243 td_clua::lua_newtable(self.lua);
244 td_clua::lua_rawset(self.lua, -3);
245
246 let name = CString::new(name).unwrap();
247 td_clua::lua_setglobal(self.lua, name.as_ptr());
248 },
249 }
250 }
251
252 pub fn create(&mut self) -> &mut LuaStruct<T> {
253 self.ensure_matetable();
254 unsafe {
255 let typeid = CString::new(T::name()).unwrap();
256 td_clua::lua_getglobal(self.lua, typeid.as_ptr());
257 if td_clua::lua_istable(self.lua, -1) {
258 td_clua::lua_newtable(self.lua);
259 "__call".push_to_lua(self.lua);
260
261 if self.light {
262 td_clua::lua_pushcfunction(self.lua, constructor_light_wrapper::<T>);
263 td_clua::lua_settable(self.lua, -3);
264 } else {
265 td_clua::lua_pushcfunction(self.lua, constructor_wrapper::<T>);
266 td_clua::lua_settable(self.lua, -3);
267 }
268
269 td_clua::lua_setmetatable(self.lua, -2);
270 }
271 td_clua::lua_pop(self.lua, 1);
272 }
273 self
274 }
275
276 pub fn def<P>(&mut self, name: &str, param: P) -> &mut LuaStruct<T>
277 where
278 P: LuaPush,
279 {
280 let tname = T::name();
281 let mut lua = Lua::from_existing_state(self.lua, false);
282 if let Some(mut table) = lua.query::<LuaTable, _>(tname.clone()) {
283 match table.query::<LuaTable, _>("__index") {
284 Some(mut index) => {
285 index.set(name, param);
286 }
287 None => {
288 let mut index = table.empty_table("__index");
289 index.set(name, param);
290 }
291 };
292 };
293 self
294 }
295
296 pub fn register(
297 &mut self,
298 name: &str,
299 func: extern "C" fn(*mut td_clua::lua_State) -> libc::c_int,
300 ) -> &mut LuaStruct<T> {
301 let tname = T::name();
302 let mut lua = Lua::from_existing_state(self.lua, false);
303 if let Some(mut table) = lua.query::<LuaTable, _>(tname.clone()) {
304 match table.query::<LuaTable, _>("__index") {
305 Some(mut index) => {
306 index.register(name, func);
307 }
308 None => {
309 let mut index = table.empty_table("__index");
310 index.register(name, func);
311 }
312 };
313 };
314 self
315 }
316}