1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use Index;
use {ffi, CheckLua, Error, IntoLua, Result, State};
use functions;
use functions::Function;
use ffi::AsCStr;
use std::ffi::CString;
use std::os::raw;
use std::rc::Rc;
use std::{mem, ops, ptr};
pub trait UserData {
const METATABLE: &'static str;
fn register(meta: &mut MetaTable) {}
}
pub trait FromLua {
unsafe fn from_lua(state: &State, idx: Index) -> Result<&Self>;
}
pub trait FromLuaMut {
unsafe fn from_lua_mut(state: &mut State, idx: Index) -> Result<&mut Self>;
}
impl<T: UserData> CheckLua for T {
unsafe fn check(state: &State, idx: Index) -> bool {
let meta = CString::new(T::METATABLE).unwrap();
!ffi::luaL_checkudata(state.pointer, idx.as_absolute(), meta.as_ptr() as _).is_null()
}
}
impl<T: UserData> FromLua for T {
unsafe fn from_lua(state: &State, idx: Index) -> Result<&Self> {
if ffi::lua_isuserdata(state.pointer, idx.as_absolute()) == 0 {
return Err(Error::Type);
}
let meta = CString::new(T::METATABLE).unwrap();
let pointer = ffi::luaL_checkudata(state.pointer, idx.as_absolute(), meta.as_ptr() as _);
if pointer.is_null() {
return Err(Error::Type);
}
Ok(mem::transmute(pointer as *const T))
}
}
impl<T: UserData> FromLuaMut for T {
unsafe fn from_lua_mut(state: &mut State, idx: Index) -> Result<&mut Self> {
if ffi::lua_isuserdata(state.pointer, idx.as_absolute()) == 0 {
return Err(Error::Type);
}
let meta = CString::new(T::METATABLE).unwrap();
let pointer = ffi::luaL_checkudata(state.pointer, idx.as_absolute(), meta.as_ptr() as _);
if pointer.is_null() {
return Err(Error::Type);
}
Ok(mem::transmute(pointer as *mut T))
}
}
#[doc(hidden)]
pub struct LuaUserDataWrapper<T>(pub T);
pub struct MetaTable(*mut ffi::lua_State);
macro_rules! impl_meta {
(pub enum $name:ident { $( $variant:ident => $meta:expr ,)+ }) => {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum MetaMethod {
$($variant,)+
}
impl MetaMethod {
#[inline]
fn as_cstr(&self) -> &str {
match *self {
$(MetaMethod::$variant => concat!($meta, "\0"),)+
}
}
}
}
}
impl_meta! {
pub enum MetaMethod {
ToString => "__tostring",
Index => "__index",
NewIndex => "__newindex",
Add => "__add",
Sub => "__sub",
Mul => "__mul",
Div => "__div",
Mod => "__mod",
Pow => "__pow",
Unm => "__unm",
Concat => "__concat",
Len => "__len",
Eq => "__eq",
Lt => "__lt",
Le => "__le",
Call => "__call",
}
}
impl MetaTable {
pub fn set<F>(&mut self, method: MetaMethod)
where
F: Function,
{
unsafe {
ffi::lua_pushstring(self.0, method.as_cstr().as_ptr() as _);
ffi::lua_pushcfunction(self.0, Some(functions::function::<F>));
ffi::lua_settable(self.0, -3);
}
}
}
impl<D> IntoLua for LuaUserDataWrapper<D>
where
D: UserData,
{
unsafe fn into_lua(self, state: &mut State) {
let data = self.0;
let ptr = ffi::lua_newuserdata(state.pointer, mem::size_of::<D>()) as *mut D;
let table_name = CString::new(D::METATABLE).unwrap();
if ffi::luaL_newmetatable(state.pointer, table_name.as_ptr() as _) == 1 {
ffi::lua_pushstring(state.pointer, "__gc\0".as_ptr() as _);
ffi::lua_pushcfunction(state.pointer, Some(__gc::<D>));
ffi::lua_settable(state.pointer, -3);
let mut meta = MetaTable(state.pointer);
D::register(&mut meta);
}
ffi::lua_setmetatable(state.pointer, -2);
ptr::copy(&data, ptr, 1);
mem::forget(data);
unsafe extern "C" fn __gc<D>(state: *mut ffi::lua_State) -> raw::c_int {
ptr::drop_in_place(ffi::lua_touserdata(state, -1) as *mut D);
0
}
}
}