@@ -12,7 +12,7 @@ use crate::string::String;
1212use crate :: table:: { Table , TablePairs } ;
1313use crate :: traits:: { FromLua , FromLuaMulti , IntoLua , IntoLuaMulti } ;
1414use crate :: types:: { MaybeSend , ValueRef } ;
15- use crate :: util:: { check_stack, get_userdata, push_string, take_userdata, StackGuard } ;
15+ use crate :: util:: { check_stack, get_userdata, push_string, short_type_name , take_userdata, StackGuard } ;
1616use crate :: value:: Value ;
1717
1818#[ cfg( feature = "async" ) ]
@@ -273,6 +273,29 @@ pub trait UserDataMethods<T> {
273273 A : FromLuaMulti ,
274274 R : IntoLuaMulti ;
275275
276+ /// Add a method which accepts `T` as the first parameter.
277+ ///
278+ /// The userdata `T` will be moved out of the userdata container. This is useful for
279+ /// methods that need to consume the userdata.
280+ ///
281+ /// The method can be called only once per userdata instance, subsequent calls will result in a
282+ /// [`Error::UserDataDestructed`] error.
283+ #[ doc( hidden) ]
284+ fn add_method_once < M , A , R > ( & mut self , name : impl Into < StdString > , method : M )
285+ where
286+ T : ' static ,
287+ M : Fn ( & Lua , T , A ) -> Result < R > + MaybeSend + ' static ,
288+ A : FromLuaMulti ,
289+ R : IntoLuaMulti ,
290+ {
291+ let name = name. into ( ) ;
292+ let method_name = format ! ( "{}.{name}" , short_type_name:: <T >( ) ) ;
293+ self . add_function ( name, move |lua, ( ud, args) : ( AnyUserData , A ) | {
294+ let this = ( ud. take ( ) ) . map_err ( |err| Error :: bad_self_argument ( & method_name, err) ) ?;
295+ method ( lua, this, args)
296+ } ) ;
297+ }
298+
276299 /// Add an async method which accepts a `&T` as the first parameter and returns [`Future`].
277300 ///
278301 /// Refer to [`add_method`] for more information about the implementation.
@@ -303,6 +326,34 @@ pub trait UserDataMethods<T> {
303326 MR : Future < Output = Result < R > > + MaybeSend + ' static ,
304327 R : IntoLuaMulti ;
305328
329+ /// Add an async method which accepts a `T` as the first parameter and returns [`Future`].
330+ ///
331+ /// The userdata `T` will be moved out of the userdata container. This is useful for
332+ /// methods that need to consume the userdata.
333+ ///
334+ /// The method can be called only once per userdata instance, subsequent calls will result in a
335+ /// [`Error::UserDataDestructed`] error.
336+ #[ cfg( feature = "async" ) ]
337+ #[ cfg_attr( docsrs, doc( cfg( feature = "async" ) ) ) ]
338+ #[ doc( hidden) ]
339+ fn add_async_method_once < M , A , MR , R > ( & mut self , name : impl Into < StdString > , method : M )
340+ where
341+ T : ' static ,
342+ M : Fn ( Lua , T , A ) -> MR + MaybeSend + ' static ,
343+ A : FromLuaMulti ,
344+ MR : Future < Output = Result < R > > + MaybeSend + ' static ,
345+ R : IntoLuaMulti ,
346+ {
347+ let name = name. into ( ) ;
348+ let method_name = format ! ( "{}.{name}" , short_type_name:: <T >( ) ) ;
349+ self . add_async_function ( name, move |lua, ( ud, args) : ( AnyUserData , A ) | {
350+ match ( ud. take ( ) ) . map_err ( |err| Error :: bad_self_argument ( & method_name, err) ) {
351+ Ok ( this) => either:: Either :: Left ( method ( lua, this, args) ) ,
352+ Err ( err) => either:: Either :: Right ( async move { Err ( err) } ) ,
353+ }
354+ } ) ;
355+ }
356+
306357 /// Add a regular method as a function which accepts generic arguments.
307358 ///
308359 /// The first argument will be a [`AnyUserData`] of type `T` if the method is called with Lua
0 commit comments