1use std::any::{Any, TypeId, type_name};
2use std::cell::{Ref, RefCell, RefMut};
3use std::ffi::{CStr, CString, c_void};
4use std::rc::Rc;
5
6use rustc_hash::{FxBuildHasher, FxHashMap};
7
8use crate::lua::function::IntoLuaFunction;
9use crate::lua::traits::ObjectLike;
10use crate::lua::types::Callback;
11use crate::lua::{self, Result, ffi::lua_State};
12use crate::lua::{Error, FromLua, FromLuaMulti, Function, Table, ToLua, ToLuaMulti, Value, ffi};
13
14thread_local! {
15 static OBJECTS: RefCell<FxHashMap<*mut c_void, Rc<dyn Any>>> =
16 const { RefCell::new(FxHashMap::with_hasher(FxBuildHasher)) };
17}
18
19pub trait UserData: 'static {
20 fn meta_methods(_: &mut MethodsBuilder) {}
21 fn methods(_: &mut MethodsBuilder) {}
22
23 fn name() -> &'static str {
24 type_name::<Self>()
25 .rsplit("::")
26 .next()
27 .unwrap_or(type_name::<Self>())
28 }
29
30 fn unique_id() -> Rc<CStr> {
31 thread_local! {
32 static IDS: RefCell<FxHashMap<TypeId, Rc<CStr>>> =
33 const { RefCell::new(FxHashMap::with_hasher(FxBuildHasher)) };
34 }
35 IDS.with_borrow_mut(|ids| {
36 ids.entry(std::any::TypeId::of::<Self>())
37 .or_insert_with(|| {
38 let cstring = CString::new(format!(
39 "{}_{:?}",
40 gmodx_macros::unique_id!(),
41 std::any::TypeId::of::<Self>()
42 ))
43 .unwrap();
44 Rc::from(cstring)
45 })
46 .clone()
47 })
48 }
49
50 fn init_methods_table(state: &lua::State) -> Table
53 where
54 Self: Sized,
55 {
56 push_methods_table::<Self>(state);
57 Table(Value::pop_from_stack(state))
58 }
59}
60
61fn push_methods_table<T: UserData>(state: &lua::State) {
62 if ffi::luaL_newmetatable(state.0, T::unique_id().as_ptr()) {
63 let methods = {
64 let mut mb = MethodsBuilder::new();
65 T::methods(&mut mb);
66 mb.build()
67 };
68 for (name, func) in methods.into_iter() {
69 state.create_function_impl(func).push_to_stack(state);
70 ffi::lua_setfield(state.0, -2, name.as_ptr());
71 }
72 }
73}
74
75impl lua::State {
76 pub fn create_userdata<T: UserData>(&self, ud: T) -> AnyUserData {
77 let ud_ptr = ffi::lua_newuserdata(self.0, 0);
79
80 OBJECTS.with_borrow_mut(|objects| {
81 let boxed: Rc<dyn Any> = Rc::new(RefCell::new(ud));
82 objects.insert(ud_ptr, boxed);
83 });
84
85 let meta_methods = {
87 let mut mb = MethodsBuilder::new();
88 T::meta_methods(&mut mb);
89 mb.build()
90 };
91 ffi::lua_createtable(self.0, 0, meta_methods.len() as i32);
92 {
93 for (name, func) in meta_methods.into_iter() {
94 self.create_function_impl(func).push_to_stack(self);
95 ffi::lua_setfield(self.0, -2, name.as_ptr());
96 }
97
98 extern "C-unwind" fn __gc(state: *mut lua_State) -> i32 {
99 let l = lua::State(state);
100 let ud_ptr = ffi::lua_touserdata(l.0, -1);
101 OBJECTS.with_borrow_mut(|objects| {
102 objects.remove(&ud_ptr);
103 });
104 0
105 }
106 ffi::lua_pushcclosure(self.0, Some(__gc), 0);
107 ffi::lua_setfield(self.0, -2, c"__gc".as_ptr());
108 }
109
110 ffi::lua_createtable(self.0, 0, 0);
112
113 ffi::lua_createtable(self.0, 0, 1);
115
116 push_methods_table::<T>(self);
118
119 ffi::lua_setfield(self.0, -2, c"__index".as_ptr()); ffi::lua_setmetatable(self.0, -2); ffi::lua_pushvalue(self.0, -1);
127 ffi::lua_setfield(self.0, -3, c"__index".as_ptr()); ffi::lua_setfield(self.0, -2, c"__newindex".as_ptr()); ffi::lua_setmetatable(self.0, -2);
134
135 AnyUserData(Value::pop_from_stack(self))
136 }
137}
138
139#[derive(Debug)]
140pub struct UserDataRef<T: UserData>(Rc<RefCell<T>>);
141
142impl<T: UserData> Clone for UserDataRef<T> {
143 fn clone(&self) -> Self {
144 UserDataRef(self.0.clone())
145 }
146}
147
148impl<T: UserData> UserDataRef<T> {
149 #[inline]
150 pub fn borrow(&self) -> Ref<'_, T> {
151 self.0.borrow()
152 }
153
154 #[inline]
155 pub fn borrow_mut(&self) -> RefMut<'_, T> {
156 self.0.borrow_mut()
157 }
158
159 #[inline]
160 pub fn try_borrow(&self) -> Result<Ref<'_, T>> {
161 self.0
162 .try_borrow()
163 .map_err(|err| Error::Message(format!("cannot borrow '{}': {}", T::name(), err)))
164 }
165
166 #[inline]
167 pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>> {
168 self.0.try_borrow_mut().map_err(|err| {
169 Error::Message(format!("cannot borrow '{}' mutably: {}", T::name(), err))
170 })
171 }
172}
173
174#[derive(Clone, Debug)]
175pub struct AnyUserData(pub(crate) Value);
176
177impl AnyUserData {
181 #[inline]
182 fn ptr(&self) -> *mut c_void {
183 ffi::lua_touserdata(self.0.thread().0, self.0.index())
184 }
185
186 #[inline]
187 pub fn is<T: UserData>(&self, _: &lua::State) -> bool {
188 OBJECTS.with_borrow(|objects| {
189 objects
190 .get(&self.ptr())
191 .is_some_and(|obj| obj.is::<RefCell<T>>())
192 })
193 }
194
195 pub fn downcast<T: UserData>(&self, _: &lua::State) -> Option<UserDataRef<T>> {
196 OBJECTS
197 .with_borrow(|objects| {
198 objects
199 .get(&self.ptr())
200 .and_then(|rc| rc.clone().downcast::<RefCell<T>>().ok())
201 })
202 .map(|rc| UserDataRef(rc))
203 }
204
205 #[inline]
206 fn from_stack_with_type(state: &lua::State, index: i32, type_name: &str) -> Result<Self> {
207 if ffi::lua_type(state.0, index) == ffi::LUA_TUSERDATA {
208 Ok(AnyUserData(Value::from_stack(state, index)))
209 } else {
210 Err(state.type_error(index, type_name))
211 }
212 }
213}
214
215impl<T: UserData> ToLua for T {
216 fn push_to_stack(self, state: &lua::State) {
217 state.create_userdata(self).push_to_stack(state);
218 }
219}
220
221impl ToLua for AnyUserData {
222 fn push_to_stack(self, state: &lua::State) {
223 self.0.push_to_stack(state);
224 }
225
226 fn to_value(self, _: &lua::State) -> Value {
227 self.0
228 }
229}
230
231impl ToLua for &AnyUserData {
232 fn push_to_stack(self, state: &lua::State) {
233 #[allow(clippy::needless_borrow)]
234 (&self.0).push_to_stack(state);
235 }
236
237 fn to_value(self, _: &lua::State) -> Value {
238 self.0.clone()
239 }
240}
241
242impl FromLua for AnyUserData {
243 #[inline]
244 fn try_from_stack(state: &lua::State, index: i32) -> Result<AnyUserData> {
245 AnyUserData::from_stack_with_type(state, index, "userdata")
246 }
247}
248
249impl<T: UserData> FromLua for UserDataRef<T> {
250 fn try_from_stack(state: &lua::State, index: i32) -> Result<Self> {
251 let any_ud = AnyUserData::from_stack_with_type(state, index, T::name())?;
252 any_ud
253 .downcast::<T>(state)
254 .ok_or_else(|| state.type_error(index, T::name()))
255 }
256}
257
258impl ObjectLike for AnyUserData {
259 fn get<V: FromLua>(&self, state: &lua::State, key: impl ToLua) -> Result<V> {
260 Table(self.0.clone()).get_protected(state, key)
261 }
262
263 fn set(&self, state: &lua::State, key: impl ToLua, value: impl ToLua) -> Result<()> {
264 Table(self.0.clone()).set_protected(state, key, value)
265 }
266
267 #[inline]
268 fn call<R: FromLuaMulti>(
269 &self,
270 state: &lua::State,
271 name: &str,
272 args: impl ToLuaMulti,
273 ) -> Result<R> {
274 let func: Function = self.get(state, name)?;
275 func.call(state, args)
276 }
277
278 #[inline]
279 fn call_method<R: FromLuaMulti>(
280 &self,
281 state: &lua::State,
282 name: &str,
283 args: impl ToLuaMulti,
284 ) -> Result<R> {
285 self.call(state, name, (self, args))
286 }
287}
288
289type Methods = Vec<(&'static CStr, Callback)>;
290
291#[derive(Default)]
292pub struct MethodsBuilder(Methods);
293
294impl MethodsBuilder {
295 fn new() -> Self {
296 Self(Vec::new())
297 }
298
299 pub fn add<F, Marker>(&mut self, name: &'static CStr, func: F)
300 where
301 F: IntoLuaFunction<Marker>,
302 {
303 let callback = func.into_callback();
304 self.0.push((name, callback));
305 }
306
307 fn build(self) -> Methods {
308 self.0
309 }
310}
311
312impl IntoIterator for MethodsBuilder {
313 type Item = (&'static CStr, Callback);
314 type IntoIter = std::vec::IntoIter<(&'static CStr, Callback)>;
315
316 fn into_iter(self) -> Self::IntoIter {
317 self.0.into_iter()
318 }
319}
320
321pub struct TypedUserData<T: UserData>(pub AnyUserData, std::marker::PhantomData<T>);
322
323impl<T: UserData> FromLua for TypedUserData<T> {
324 fn try_from_stack(state: &lua::State, index: i32) -> Result<Self> {
325 let any = AnyUserData::from_stack_with_type(state, index, T::name())?;
326 any.downcast::<T>(state)
328 .ok_or_else(|| state.type_error(index, T::name()))?;
329 Ok(TypedUserData(any, std::marker::PhantomData))
330 }
331}
332
333impl<T: UserData> std::ops::Deref for TypedUserData<T> {
334 type Target = AnyUserData;
335
336 fn deref(&self) -> &Self::Target {
337 &self.0
338 }
339}
340
341impl<T: UserData> std::ops::DerefMut for TypedUserData<T> {
342 fn deref_mut(&mut self) -> &mut Self::Target {
343 &mut self.0
344 }
345}