1use std::any::Any;
2use std::cell::{Cell, RefCell};
3use std::marker::PhantomData;
4use std::mem;
5use std::os::raw::{c_int, c_void};
6use std::rc::Rc;
7
8#[cfg(feature = "serialize")]
9use serde::Serialize;
10
11use crate::error::{Error, Result};
12use crate::ffi;
13use crate::function::Function;
14use crate::lua::Lua;
15use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
16use crate::userdata::{
17 AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
18};
19use crate::util::{
20 assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field,
21 take_userdata, StackGuard,
22};
23use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
24
25#[cfg(feature = "lua54")]
26use crate::userdata::USER_VALUE_MAXSLOT;
27
28#[cfg(feature = "async")]
29use {
30 crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
31 futures_core::future::Future,
32 futures_util::future::{self, TryFutureExt},
33};
34
35pub struct Scope<'lua, 'scope> {
42 lua: &'lua Lua,
43 destructors: RefCell<Vec<(LuaRef<'lua>, DestructorCallback<'lua>)>>,
44 _scope_invariant: PhantomData<Cell<&'scope ()>>,
45}
46
47type DestructorCallback<'lua> = Box<dyn Fn(LuaRef<'lua>) -> Vec<Box<dyn Any>> + 'lua>;
48
49impl<'lua, 'scope> Scope<'lua, 'scope> {
50 pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
51 Scope {
52 lua,
53 destructors: RefCell::new(Vec::new()),
54 _scope_invariant: PhantomData,
55 }
56 }
57
58 pub fn create_function<'callback, A, R, F>(&'callback self, func: F) -> Result<Function<'lua>>
66 where
67 A: FromLuaMulti<'callback>,
68 R: ToLuaMulti<'callback>,
69 F: 'scope + Fn(&'callback Lua, A) -> Result<R>,
70 {
71 unsafe {
81 self.create_callback(Box::new(move |lua, args| {
82 func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
83 }))
84 }
85 }
86
87 pub fn create_function_mut<'callback, A, R, F>(
96 &'callback self,
97 func: F,
98 ) -> Result<Function<'lua>>
99 where
100 A: FromLuaMulti<'callback>,
101 R: ToLuaMulti<'callback>,
102 F: 'scope + FnMut(&'callback Lua, A) -> Result<R>,
103 {
104 let func = RefCell::new(func);
105 self.create_function(move |lua, args| {
106 (*func
107 .try_borrow_mut()
108 .map_err(|_| Error::RecursiveMutCallback)?)(lua, args)
109 })
110 }
111
112 #[cfg(feature = "async")]
123 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
124 pub fn create_async_function<'callback, A, R, F, FR>(
125 &'callback self,
126 func: F,
127 ) -> Result<Function<'lua>>
128 where
129 A: FromLuaMulti<'callback>,
130 R: ToLuaMulti<'callback>,
131 F: 'scope + Fn(&'callback Lua, A) -> FR,
132 FR: 'callback + Future<Output = Result<R>>,
133 {
134 unsafe {
135 self.create_async_callback(Box::new(move |lua, args| {
136 let args = match A::from_lua_multi(args, lua) {
137 Ok(args) => args,
138 Err(e) => return Box::pin(future::err(e)),
139 };
140 Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua))))
141 }))
142 }
143 }
144
145 pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
155 where
156 T: 'static + UserData,
157 {
158 self.create_userdata_inner(UserDataCell::new(data))
159 }
160
161 #[cfg(feature = "serialize")]
173 #[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
174 pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
175 where
176 T: 'static + UserData + Serialize,
177 {
178 self.create_userdata_inner(UserDataCell::new_ser(data))
179 }
180
181 fn create_userdata_inner<T>(&self, data: UserDataCell<T>) -> Result<AnyUserData<'lua>>
182 where
183 T: 'static + UserData,
184 {
185 unsafe {
188 let ud = self.lua.make_userdata(data)?;
189
190 #[cfg(any(feature = "lua51", feature = "luajit"))]
191 let newtable = self.lua.create_table()?;
192 let destructor: DestructorCallback = Box::new(move |ud| {
193 let state = ud.lua.state;
194 let _sg = StackGuard::new(state);
195 assert_stack(state, 2);
196
197 if ud.lua.push_userdata_ref(&ud).is_err() {
199 return vec![];
200 }
201
202 #[cfg(feature = "lua54")]
204 for i in 1..=USER_VALUE_MAXSLOT {
205 ffi::lua_pushnil(state);
206 ffi::lua_setiuservalue(state, -2, i as c_int);
207 }
208 #[cfg(any(feature = "lua53", feature = "lua52", feature = "luau", feature = "lua-factorio"))]
209 {
210 ffi::lua_pushnil(state);
211 ffi::lua_setuservalue(state, -2);
212 }
213 #[cfg(any(feature = "lua51", feature = "luajit"))]
214 {
215 ud.lua.push_ref(&newtable.0);
216 ffi::lua_setuservalue(state, -2);
217 }
218
219 vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
220 });
221 self.destructors
222 .borrow_mut()
223 .push((ud.0.clone(), destructor));
224
225 Ok(ud)
226 }
227 }
228
229 pub fn create_nonstatic_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
253 where
254 T: 'scope + UserData,
255 {
256 let data = Rc::new(RefCell::new(data));
257
258 fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
264 scope: &Scope<'lua, 'scope>,
265 data: Rc<RefCell<T>>,
266 ud_ptr: *const c_void,
267 method: NonStaticMethod<'callback, T>,
268 ) -> Result<Function<'lua>> {
269 let check_ud_type = move |lua: &'callback Lua, value| {
277 if let Some(Value::UserData(ud)) = value {
278 unsafe {
279 let _sg = StackGuard::new(lua.state);
280 check_stack(lua.state, 2)?;
281 lua.push_userdata_ref(&ud.0)?;
282 if get_userdata(lua.state, -1) as *const _ == ud_ptr {
283 return Ok(());
284 }
285 }
286 };
287 Err(Error::UserDataTypeMismatch)
288 };
289
290 match method {
291 NonStaticMethod::Method(method) => {
292 let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
293 check_ud_type(lua, args.pop_front())?;
294 let data = data.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
295 method(lua, &*data, args)
296 });
297 unsafe { scope.create_callback(f) }
298 }
299 NonStaticMethod::MethodMut(method) => {
300 let method = RefCell::new(method);
301 let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
302 check_ud_type(lua, args.pop_front())?;
303 let mut method = method
304 .try_borrow_mut()
305 .map_err(|_| Error::RecursiveMutCallback)?;
306 let mut data = data
307 .try_borrow_mut()
308 .map_err(|_| Error::UserDataBorrowMutError)?;
309 (*method)(lua, &mut *data, args)
310 });
311 unsafe { scope.create_callback(f) }
312 }
313 NonStaticMethod::Function(function) => unsafe { scope.create_callback(function) },
314 NonStaticMethod::FunctionMut(function) => {
315 let function = RefCell::new(function);
316 let f = Box::new(move |lua, args| {
317 (*function
318 .try_borrow_mut()
319 .map_err(|_| Error::RecursiveMutCallback)?)(
320 lua, args
321 )
322 });
323 unsafe { scope.create_callback(f) }
324 }
325 }
326 }
327
328 let mut ud_fields = NonStaticUserDataFields::default();
329 let mut ud_methods = NonStaticUserDataMethods::default();
330 T::add_fields(&mut ud_fields);
331 T::add_methods(&mut ud_methods);
332
333 unsafe {
334 let lua = self.lua;
335 let _sg = StackGuard::new(lua.state);
336 check_stack(lua.state, 13)?;
337
338 #[cfg(not(feature = "luau"))]
339 #[allow(clippy::let_and_return)]
340 let ud_ptr = protect_lua!(lua.state, 0, 1, |state| {
341 let ud =
342 ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<Rc<RefCell<T>>>>());
343
344 #[cfg(any(feature = "lua51", feature = "luajit"))]
346 {
347 ffi::lua_newtable(state);
348 ffi::lua_setuservalue(state, -2);
349 }
350
351 ud
352 })?;
353 #[cfg(feature = "luau")]
354 let ud_ptr = {
355 crate::util::push_userdata::<UserDataCell<Rc<RefCell<T>>>>(
356 lua.state,
357 UserDataCell::new(data.clone()),
358 true,
359 )?;
360 ffi::lua_touserdata(lua.state, -1)
361 };
362
363 let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
365 push_table(lua.state, 0, meta_methods_nrec as c_int, true)?;
366
367 for (k, m) in ud_methods.meta_methods {
368 let data = data.clone();
369 lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
370 rawset_field(lua.state, -2, k.validate()?.name())?;
371 }
372 for (k, f) in ud_fields.meta_fields {
373 lua.push_value(f(mem::transmute(lua))?)?;
374 rawset_field(lua.state, -2, k.validate()?.name())?;
375 }
376 let metatable_index = ffi::lua_absindex(lua.state, -1);
377
378 let mut field_getters_index = None;
379 let field_getters_nrec = ud_fields.field_getters.len();
380 if field_getters_nrec > 0 {
381 push_table(lua.state, 0, field_getters_nrec as c_int, true)?;
382 for (k, m) in ud_fields.field_getters {
383 let data = data.clone();
384 lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
385 rawset_field(lua.state, -2, &k)?;
386 }
387 field_getters_index = Some(ffi::lua_absindex(lua.state, -1));
388 }
389
390 let mut field_setters_index = None;
391 let field_setters_nrec = ud_fields.field_setters.len();
392 if field_setters_nrec > 0 {
393 push_table(lua.state, 0, field_setters_nrec as c_int, true)?;
394 for (k, m) in ud_fields.field_setters {
395 let data = data.clone();
396 lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
397 rawset_field(lua.state, -2, &k)?;
398 }
399 field_setters_index = Some(ffi::lua_absindex(lua.state, -1));
400 }
401
402 let mut methods_index = None;
403 let methods_nrec = ud_methods.methods.len();
404 if methods_nrec > 0 {
405 push_table(lua.state, 0, methods_nrec as c_int, true)?;
407 for (k, m) in ud_methods.methods {
408 let data = data.clone();
409 lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
410 rawset_field(lua.state, -2, &k)?;
411 }
412 methods_index = Some(ffi::lua_absindex(lua.state, -1));
413 }
414
415 init_userdata_metatable::<UserDataCell<Rc<RefCell<T>>>>(
416 lua.state,
417 metatable_index,
418 field_getters_index,
419 field_setters_index,
420 methods_index,
421 )?;
422
423 let count = field_getters_index.map(|_| 1).unwrap_or(0)
424 + field_setters_index.map(|_| 1).unwrap_or(0)
425 + methods_index.map(|_| 1).unwrap_or(0);
426 ffi::lua_pop(lua.state, count);
427
428 let mt_ptr = ffi::lua_topointer(lua.state, -1);
429 #[cfg(not(feature = "luau"))]
431 std::ptr::write(ud_ptr as _, UserDataCell::new(data));
432 ffi::lua_setmetatable(lua.state, -2);
433 let ud = AnyUserData(lua.pop_ref());
434 lua.register_userdata_metatable(mt_ptr, None);
435
436 #[cfg(any(feature = "lua51", feature = "luajit"))]
437 let newtable = lua.create_table()?;
438 let destructor: DestructorCallback = Box::new(move |ud| {
439 let state = ud.lua.state;
440 let _sg = StackGuard::new(state);
441 assert_stack(state, 2);
442
443 if ud.lua.push_userdata_ref(&ud).is_err() {
445 return vec![];
446 }
447
448 ffi::lua_getmetatable(state, -1);
450 let mt_ptr = ffi::lua_topointer(state, -1);
451 ffi::lua_pop(state, 1);
452 ud.lua.deregister_userdata_metatable(mt_ptr);
453
454 #[cfg(feature = "lua54")]
456 for i in 1..=USER_VALUE_MAXSLOT {
457 ffi::lua_pushnil(state);
458 ffi::lua_setiuservalue(state, -2, i as c_int);
459 }
460 #[cfg(any(feature = "lua53", feature = "lua52", feature = "luau", feature = "lua-factorio"))]
461 {
462 ffi::lua_pushnil(state);
463 ffi::lua_setuservalue(state, -2);
464 }
465 #[cfg(any(feature = "lua51", feature = "luajit"))]
466 {
467 ud.lua.push_ref(&newtable.0);
468 ffi::lua_setuservalue(state, -2);
469 }
470
471 unsafe fn seal<T>(t: T) -> Box<dyn FnOnce() + 'static> {
473 let f: Box<dyn FnOnce()> = Box::new(move || drop(t));
474 mem::transmute(f)
475 }
476
477 let ud = Box::new(seal(take_userdata::<UserDataCell<Rc<RefCell<T>>>>(state)));
478 vec![ud]
479 });
480 self.destructors
481 .borrow_mut()
482 .push((ud.0.clone(), destructor));
483
484 Ok(ud)
485 }
486 }
487
488 unsafe fn create_callback<'callback>(
494 &self,
495 f: Callback<'callback, 'scope>,
496 ) -> Result<Function<'lua>> {
497 let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'lua, 'static>>(f);
498 let f = self.lua.create_callback(f)?;
499
500 let destructor: DestructorCallback = Box::new(|f| {
501 let state = f.lua.state;
502 let _sg = StackGuard::new(state);
503 assert_stack(state, 3);
504
505 f.lua.push_ref(&f);
506
507 ffi::lua_getupvalue(state, -1, 1);
510 let ud = take_userdata::<CallbackUpvalue>(state);
511 ffi::lua_pushnil(state);
512 ffi::lua_setupvalue(state, -2, 1);
513
514 vec![Box::new(ud)]
515 });
516 self.destructors
517 .borrow_mut()
518 .push((f.0.clone(), destructor));
519
520 Ok(f)
521 }
522
523 #[cfg(feature = "async")]
524 unsafe fn create_async_callback<'callback>(
525 &self,
526 f: AsyncCallback<'callback, 'scope>,
527 ) -> Result<Function<'lua>> {
528 let f = mem::transmute::<AsyncCallback<'callback, 'scope>, AsyncCallback<'lua, 'static>>(f);
529 let f = self.lua.create_async_callback(f)?;
530
531 let get_poll_str = self.lua.create_string("get_poll")?;
533 let poll_str = self.lua.create_string("poll")?;
534 let destructor: DestructorCallback = Box::new(move |f| {
535 let state = f.lua.state;
536 let _sg = StackGuard::new(state);
537 assert_stack(state, 5);
538
539 f.lua.push_ref(&f);
540
541 #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua-factorio"))]
545 ffi::lua_getupvalue(state, -1, 1);
546 #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
547 ffi::lua_getfenv(state, -1);
548
549 f.lua.push_ref(&get_poll_str.0);
551 ffi::lua_rawget(state, -2);
552
553 ffi::lua_getupvalue(state, -1, 1);
555 let upvalue1 = take_userdata::<AsyncCallbackUpvalue>(state);
556 ffi::lua_pushnil(state);
557 ffi::lua_setupvalue(state, -2, 1);
558
559 ffi::lua_pop(state, 1);
560 let mut data: Vec<Box<dyn Any>> = vec![Box::new(upvalue1)];
561
562 f.lua.push_ref(&poll_str.0);
564 if ffi::lua_rawget(state, -2) == ffi::LUA_TFUNCTION {
565 ffi::lua_getupvalue(state, -1, 1);
566 let upvalue2 = take_userdata::<AsyncPollUpvalue>(state);
567 ffi::lua_pushnil(state);
568 ffi::lua_setupvalue(state, -2, 1);
569 data.push(Box::new(upvalue2));
570 }
571
572 data
573 });
574 self.destructors
575 .borrow_mut()
576 .push((f.0.clone(), destructor));
577
578 Ok(f)
579 }
580}
581
582impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
583 fn drop(&mut self) {
584 let to_drop = self
590 .destructors
591 .get_mut()
592 .drain(..)
593 .flat_map(|(r, dest)| dest(r))
594 .collect::<Vec<_>>();
595
596 drop(to_drop);
597 }
598}
599
600enum NonStaticMethod<'lua, T> {
601 Method(Box<dyn Fn(&'lua Lua, &T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
602 MethodMut(Box<dyn FnMut(&'lua Lua, &mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
603 Function(Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
604 FunctionMut(Box<dyn FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
605}
606
607struct NonStaticUserDataMethods<'lua, T: UserData> {
608 methods: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
609 meta_methods: Vec<(MetaMethod, NonStaticMethod<'lua, T>)>,
610}
611
612impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
613 fn default() -> NonStaticUserDataMethods<'lua, T> {
614 NonStaticUserDataMethods {
615 methods: Vec::new(),
616 meta_methods: Vec::new(),
617 }
618 }
619}
620
621impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> {
622 fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
623 where
624 S: AsRef<[u8]> + ?Sized,
625 A: FromLuaMulti<'lua>,
626 R: ToLuaMulti<'lua>,
627 M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
628 {
629 self.methods.push((
630 name.as_ref().to_vec(),
631 NonStaticMethod::Method(Box::new(move |lua, ud, args| {
632 method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
633 })),
634 ));
635 }
636
637 fn add_method_mut<S, A, R, M>(&mut self, name: &S, mut method: M)
638 where
639 S: AsRef<[u8]> + ?Sized,
640 A: FromLuaMulti<'lua>,
641 R: ToLuaMulti<'lua>,
642 M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
643 {
644 self.methods.push((
645 name.as_ref().to_vec(),
646 NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
647 method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
648 })),
649 ));
650 }
651
652 #[cfg(feature = "async")]
653 fn add_async_method<S, A, R, M, MR>(&mut self, _name: &S, _method: M)
654 where
655 T: Clone,
656 S: AsRef<[u8]> + ?Sized,
657 A: FromLuaMulti<'lua>,
658 R: ToLuaMulti<'lua>,
659 M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
660 MR: 'lua + Future<Output = Result<R>>,
661 {
662 mlua_panic!("asynchronous methods are not supported for non-static userdata")
665 }
666
667 fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
668 where
669 S: AsRef<[u8]> + ?Sized,
670 A: FromLuaMulti<'lua>,
671 R: ToLuaMulti<'lua>,
672 F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
673 {
674 self.methods.push((
675 name.as_ref().to_vec(),
676 NonStaticMethod::Function(Box::new(move |lua, args| {
677 function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
678 })),
679 ));
680 }
681
682 fn add_function_mut<S, A, R, F>(&mut self, name: &S, mut function: F)
683 where
684 S: AsRef<[u8]> + ?Sized,
685 A: FromLuaMulti<'lua>,
686 R: ToLuaMulti<'lua>,
687 F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
688 {
689 self.methods.push((
690 name.as_ref().to_vec(),
691 NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
692 function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
693 })),
694 ));
695 }
696
697 #[cfg(feature = "async")]
698 fn add_async_function<S, A, R, F, FR>(&mut self, _name: &S, _function: F)
699 where
700 S: AsRef<[u8]> + ?Sized,
701 A: FromLuaMulti<'lua>,
702 R: ToLuaMulti<'lua>,
703 F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
704 FR: 'lua + Future<Output = Result<R>>,
705 {
706 mlua_panic!("asynchronous functions are not supported for non-static userdata")
709 }
710
711 fn add_meta_method<S, A, R, M>(&mut self, meta: S, method: M)
712 where
713 S: Into<MetaMethod>,
714 A: FromLuaMulti<'lua>,
715 R: ToLuaMulti<'lua>,
716 M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
717 {
718 self.meta_methods.push((
719 meta.into(),
720 NonStaticMethod::Method(Box::new(move |lua, ud, args| {
721 method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
722 })),
723 ));
724 }
725
726 fn add_meta_method_mut<S, A, R, M>(&mut self, meta: S, mut method: M)
727 where
728 S: Into<MetaMethod>,
729 A: FromLuaMulti<'lua>,
730 R: ToLuaMulti<'lua>,
731 M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
732 {
733 self.meta_methods.push((
734 meta.into(),
735 NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
736 method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
737 })),
738 ));
739 }
740
741 #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
742 fn add_async_meta_method<S, A, R, M, MR>(&mut self, _meta: S, _method: M)
743 where
744 T: Clone,
745 S: Into<MetaMethod>,
746 A: FromLuaMulti<'lua>,
747 R: ToLuaMulti<'lua>,
748 M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
749 MR: 'lua + Future<Output = Result<R>>,
750 {
751 mlua_panic!("asynchronous meta methods are not supported for non-static userdata")
754 }
755
756 fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
757 where
758 S: Into<MetaMethod>,
759 A: FromLuaMulti<'lua>,
760 R: ToLuaMulti<'lua>,
761 F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
762 {
763 self.meta_methods.push((
764 meta.into(),
765 NonStaticMethod::Function(Box::new(move |lua, args| {
766 function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
767 })),
768 ));
769 }
770
771 fn add_meta_function_mut<S, A, R, F>(&mut self, meta: S, mut function: F)
772 where
773 S: Into<MetaMethod>,
774 A: FromLuaMulti<'lua>,
775 R: ToLuaMulti<'lua>,
776 F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
777 {
778 self.meta_methods.push((
779 meta.into(),
780 NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
781 function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
782 })),
783 ));
784 }
785
786 #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
787 fn add_async_meta_function<S, A, R, F, FR>(&mut self, _meta: S, _function: F)
788 where
789 S: Into<MetaMethod>,
790 A: FromLuaMulti<'lua>,
791 R: ToLuaMulti<'lua>,
792 F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
793 FR: 'lua + Future<Output = Result<R>>,
794 {
795 mlua_panic!("asynchronous meta functions are not supported for non-static userdata")
798 }
799}
800
801struct NonStaticUserDataFields<'lua, T: UserData> {
802 field_getters: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
803 field_setters: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
804 #[allow(clippy::type_complexity)]
805 meta_fields: Vec<(MetaMethod, Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>>>)>,
806}
807
808impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
809 fn default() -> NonStaticUserDataFields<'lua, T> {
810 NonStaticUserDataFields {
811 field_getters: Vec::new(),
812 field_setters: Vec::new(),
813 meta_fields: Vec::new(),
814 }
815 }
816}
817
818impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua, T> {
819 fn add_field_method_get<S, R, M>(&mut self, name: &S, method: M)
820 where
821 S: AsRef<[u8]> + ?Sized,
822 R: ToLua<'lua>,
823 M: 'static + MaybeSend + Fn(&'lua Lua, &T) -> Result<R>,
824 {
825 self.field_getters.push((
826 name.as_ref().to_vec(),
827 NonStaticMethod::Method(Box::new(move |lua, ud, _| {
828 method(lua, ud)?.to_lua_multi(lua)
829 })),
830 ));
831 }
832
833 fn add_field_method_set<S, A, M>(&mut self, name: &S, mut method: M)
834 where
835 S: AsRef<[u8]> + ?Sized,
836 A: FromLua<'lua>,
837 M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<()>,
838 {
839 self.field_setters.push((
840 name.as_ref().to_vec(),
841 NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
842 method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
843 })),
844 ));
845 }
846
847 fn add_field_function_get<S, R, F>(&mut self, name: &S, function: F)
848 where
849 S: AsRef<[u8]> + ?Sized,
850 R: ToLua<'lua>,
851 F: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R>,
852 {
853 self.field_getters.push((
854 name.as_ref().to_vec(),
855 NonStaticMethod::Function(Box::new(move |lua, args| {
856 function(lua, AnyUserData::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
857 })),
858 ));
859 }
860
861 fn add_field_function_set<S, A, F>(&mut self, name: &S, mut function: F)
862 where
863 S: AsRef<[u8]> + ?Sized,
864 A: FromLua<'lua>,
865 F: 'static + MaybeSend + FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()>,
866 {
867 self.field_setters.push((
868 name.as_ref().to_vec(),
869 NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
870 let (ud, val) = <_>::from_lua_multi(args, lua)?;
871 function(lua, ud, val)?.to_lua_multi(lua)
872 })),
873 ));
874 }
875
876 fn add_meta_field_with<S, R, F>(&mut self, meta: S, f: F)
877 where
878 S: Into<MetaMethod>,
879 F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
880 R: ToLua<'lua>,
881 {
882 let meta = meta.into();
883 self.meta_fields.push((
884 meta.clone(),
885 Box::new(move |lua| {
886 let value = f(lua)?.to_lua(lua)?;
887 if meta == MetaMethod::Index || meta == MetaMethod::NewIndex {
888 match value {
889 Value::Nil | Value::Table(_) | Value::Function(_) => {}
890 _ => {
891 return Err(Error::MetaMethodTypeError {
892 method: meta.to_string(),
893 type_name: value.type_name(),
894 message: Some("expected nil, table or function".to_string()),
895 })
896 }
897 }
898 }
899 Ok(value)
900 }),
901 ));
902 }
903}