Skip to content

Commit 87dc0c1

Browse files
committed
mlua_derive: Make userdata_impl independent from module where UserData is defined
Before this change `#[mlua::userdata_impl]` was working only when it's placed together with `#[derive(UserData)]`, in the same module. Now it's flexible to allow splitting type definition and Lua binding code. Closes #726
1 parent 7539003 commit 87dc0c1

3 files changed

Lines changed: 45 additions & 4 deletions

File tree

mlua_derive/src/userdata/mod.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,25 +132,36 @@ pub fn userdata_type(item: TokenStream) -> TokenStream {
132132
}
133133

134134
let registration_type_name = format_ident!("__MluaUserDataRegistration_{type_name}");
135+
let registration_fn_name = format_ident!("__mlua_userdata_registration");
135136
let register_fields_fn_name = format_ident!("__mlua_register_{type_name}_fields");
136137

137138
let output = quote! {
138139
#[doc(hidden)]
139140
#[allow(non_camel_case_types)]
140-
struct #registration_type_name {
141+
pub(crate) struct #registration_type_name {
141142
register: fn(&mut ::mlua::userdata::UserDataRegistry<#type_name>),
142143
}
143144

144145
::mlua::__inventory::collect!(#registration_type_name);
145146

147+
impl #type_name {
148+
#[doc(hidden)]
149+
#[allow(dead_code)]
150+
pub(crate) const fn #registration_fn_name(
151+
register: fn(&mut ::mlua::userdata::UserDataRegistry<Self>),
152+
) -> #registration_type_name {
153+
#registration_type_name { register }
154+
}
155+
}
156+
146157
#[allow(non_snake_case)]
147158
fn #register_fields_fn_name(registry: &mut ::mlua::userdata::UserDataRegistry<#type_name>) {
148159
use ::mlua::userdata::UserDataFields as _;
149160
#(#field_registrations)*
150161
}
151162

152163
::mlua::__inventory::submit! {
153-
#registration_type_name { register: #register_fields_fn_name }
164+
#type_name::#registration_fn_name(#register_fields_fn_name)
154165
}
155166

156167
impl ::mlua::userdata::UserData for #type_name {

mlua_derive/src/userdata/userdata_impl.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ pub fn userdata_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
323323
static COUNTER: AtomicUsize = AtomicUsize::new(0);
324324
let unique_suffix = COUNTER.fetch_add(1, Ordering::Relaxed);
325325
let register_fn_name = format_ident!("__mlua_register_{type_name}_{unique_suffix}");
326-
let registration_type_name = format_ident!("__MluaUserDataRegistration_{type_name}");
326+
let registration_fn_name = format_ident!("__mlua_userdata_registration");
327327

328328
let mut registration_calls = Vec::new();
329329
for item in &input.items {
@@ -522,7 +522,7 @@ pub fn userdata_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
522522
}
523523

524524
::mlua::__inventory::submit! {
525-
#registration_type_name { register: #register_fn_name }
525+
#type_path::#registration_fn_name(#register_fn_name)
526526
}
527527

528528
#input

tests/userdata_macro.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,36 @@ fn test_wildcard_params() {
569569
.unwrap();
570570
}
571571

572+
mod separate_definition {
573+
use mlua::UserData;
574+
575+
#[derive(UserData)]
576+
pub(super) struct SeparateModuleUserData;
577+
}
578+
579+
mod separate_implementation {
580+
use super::separate_definition::SeparateModuleUserData;
581+
582+
#[mlua::userdata_impl]
583+
impl SeparateModuleUserData {
584+
#[lua(infallible)]
585+
fn answer(&self) -> i32 {
586+
42
587+
}
588+
}
589+
}
590+
591+
#[test]
592+
fn test_userdata_impl_in_separate_module() {
593+
let lua = Lua::new();
594+
let userdata = lua
595+
.create_userdata(separate_definition::SeparateModuleUserData)
596+
.unwrap();
597+
lua.globals().set("userdata", userdata).unwrap();
598+
599+
assert_eq!(lua.load("return userdata:answer()").eval::<i32>().unwrap(), 42);
600+
}
601+
572602
#[cfg(feature = "async")]
573603
mod async_tests {
574604
use mlua::{Lua, Result, UserData};

0 commit comments

Comments
 (0)