From 6e7d6c78eda36858865f99e95d1a8d97627e4a55 Mon Sep 17 00:00:00 2001 From: Alex Orlenko Date: Mon, 18 May 2026 23:21:37 +0100 Subject: [PATCH] mlua_derive: Group functionality in modules Create new `chunk`, `userdata`, `module` modules. --- mlua_derive/src/{chunk.rs => chunk/mod.rs} | 67 ++++++++- mlua_derive/src/{ => chunk}/token.rs | 0 mlua_derive/src/from_lua.rs | 24 ++-- mlua_derive/src/lib.rs | 134 +----------------- mlua_derive/src/module.rs | 68 +++++++++ mlua_derive/src/{ => userdata}/attr.rs | 0 .../src/{userdata.rs => userdata/mod.rs} | 5 +- .../src/{ => userdata}/userdata_impl.rs | 2 +- 8 files changed, 151 insertions(+), 149 deletions(-) rename mlua_derive/src/{chunk.rs => chunk/mod.rs} (54%) rename mlua_derive/src/{ => chunk}/token.rs (100%) create mode 100644 mlua_derive/src/module.rs rename mlua_derive/src/{ => userdata}/attr.rs (100%) rename mlua_derive/src/{userdata.rs => userdata/mod.rs} (98%) rename mlua_derive/src/{ => userdata}/userdata_impl.rs (99%) diff --git a/mlua_derive/src/chunk.rs b/mlua_derive/src/chunk/mod.rs similarity index 54% rename from mlua_derive/src/chunk.rs rename to mlua_derive/src/chunk/mod.rs index 1b5ea0c..50306e8 100644 --- a/mlua_derive/src/chunk.rs +++ b/mlua_derive/src/chunk/mod.rs @@ -2,9 +2,11 @@ use std::ops::Deref; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; -use quote::ToTokens; +use quote::{ToTokens, quote}; -use crate::token::{Pos, Token, Tokens}; +use self::token::{Pos, Token, Tokens}; + +mod token; #[derive(Debug, Clone)] pub(crate) struct Capture(Token); @@ -95,11 +97,64 @@ impl Chunk { } } - pub(crate) fn source(&self) -> &str { - &self.source - } - pub(crate) fn captures(&self) -> &[Capture] { self.caps.captures() } + + pub(crate) fn expand(&self) -> TokenStream2 { + let source = &self.source; + + let caps_len = self.captures().len(); + let caps = self.captures().iter().map(|cap| { + let cap_name = cap.name(); + quote! { env.raw_set(#cap_name, #cap)?; } + }); + + quote! {{ + use mlua::{AsChunk, ChunkMode, Lua, Result, Table}; + use ::std::borrow::Cow; + use ::std::cell::Cell; + use ::std::io::Result as IoResult; + + struct InnerChunk Result>(Cell>); + + impl AsChunk for InnerChunk + where + F: FnOnce(&Lua) -> Result
, + { + fn environment(&self, lua: &Lua) -> Result> { + if #caps_len > 0 { + if let Some(make_env) = self.0.take() { + return make_env(lua).map(Some); + } + } + Ok(None) + } + + fn mode(&self) -> Option { + Some(ChunkMode::Text) + } + + fn source<'a>(&self) -> IoResult> { + Ok(Cow::Borrowed((#source).as_bytes())) + } + } + + let make_env = move |lua: &Lua| -> Result
{ + let globals = lua.globals(); + let env = lua.create_table()?; + let meta = lua.create_table()?; + meta.raw_set("__index", &globals)?; + meta.raw_set("__newindex", &globals)?; + + // Add captured variables + #(#caps)* + + env.set_metatable(Some(meta))?; + Ok(env) + }; + + InnerChunk(Cell::new(Some(make_env))) + }} + } } diff --git a/mlua_derive/src/token.rs b/mlua_derive/src/chunk/token.rs similarity index 100% rename from mlua_derive/src/token.rs rename to mlua_derive/src/chunk/token.rs diff --git a/mlua_derive/src/from_lua.rs b/mlua_derive/src/from_lua.rs index fbd2f22..2ca6918 100644 --- a/mlua_derive/src/from_lua.rs +++ b/mlua_derive/src/from_lua.rs @@ -13,19 +13,19 @@ pub fn from_lua(input: TokenStream) -> TokenStream { }; quote! { - impl #impl_generics ::mlua::FromLua for #ident #ty_generics #where_clause { - #[inline] - fn from_lua(value: ::mlua::Value, _: &::mlua::Lua) -> ::mlua::Result { - match value { - ::mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), - _ => Err(::mlua::Error::FromLuaConversionError { - from: value.type_name(), - to: #ident_str.to_string(), - message: None, - }), - } + impl #impl_generics ::mlua::FromLua for #ident #ty_generics #where_clause { + #[inline] + fn from_lua(value: ::mlua::Value, _: &::mlua::Lua) -> ::mlua::Result { + match value { + ::mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), + _ => Err(::mlua::Error::FromLuaConversionError { + from: value.type_name(), + to: #ident_str.to_string(), + message: None, + }), + } + } } - } } .into() } diff --git a/mlua_derive/src/lib.rs b/mlua_derive/src/lib.rs index e943d9f..0a4f8af 100644 --- a/mlua_derive/src/lib.rs +++ b/mlua_derive/src/lib.rs @@ -1,8 +1,6 @@ use proc_macro::TokenStream; -use proc_macro2::{Ident, Span}; -use quote::quote; -use syn::meta::ParseNestedMeta; -use syn::{ItemFn, LitStr, Result, parse_macro_input}; + +mod module; #[cfg(feature = "macros")] use {crate::chunk::Chunk, proc_macro_error2::proc_macro_error}; @@ -17,132 +15,16 @@ macro_rules! try_compile { }; } -#[derive(Default)] -struct ModuleAttributes { - name: Option, - skip_memory_check: bool, -} - -impl ModuleAttributes { - fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> { - if meta.path.is_ident("name") { - match meta.value() { - Ok(value) => { - self.name = Some(value.parse::()?.parse()?); - } - Err(_) => { - return Err(meta.error("`name` attribute must have a value")); - } - } - } else if meta.path.is_ident("skip_memory_check") { - if meta.value().is_ok() { - return Err(meta.error("`skip_memory_check` attribute have no values")); - } - self.skip_memory_check = true; - } else { - return Err(meta.error("unsupported module attribute")); - } - Ok(()) - } -} - #[proc_macro_attribute] pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream { - let mut args = ModuleAttributes::default(); - if !attr.is_empty() { - let args_parser = syn::meta::parser(|meta| args.parse(meta)); - parse_macro_input!(attr with args_parser); - } - - let func = parse_macro_input!(item as ItemFn); - let func_name = &func.sig.ident; - let module_name = args.name.unwrap_or_else(|| func_name.clone()); - let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site()); - let skip_memory_check = if args.skip_memory_check { - quote! { lua.skip_memory_check(true); } - } else { - quote! {} - }; - - let wrapped = quote! { - mlua::require_module_feature!(); - - #func - - #[unsafe(no_mangle)] - unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> ::std::os::raw::c_int { - mlua::Lua::entrypoint1(state, move |lua| { - #skip_memory_check - #func_name(lua) - }) - } - }; - - wrapped.into() + module::lua_module(attr, item) } #[cfg(feature = "macros")] #[proc_macro] #[proc_macro_error] pub fn chunk(input: TokenStream) -> TokenStream { - let chunk = Chunk::new(input); - - let source = chunk.source(); - - let caps_len = chunk.captures().len(); - let caps = chunk.captures().iter().map(|cap| { - let cap_name = cap.name(); - quote! { env.raw_set(#cap_name, #cap)?; } - }); - - let wrapped_code = quote! {{ - use mlua::{AsChunk, ChunkMode, Lua, Result, Table}; - use ::std::borrow::Cow; - use ::std::cell::Cell; - use ::std::io::Result as IoResult; - - struct InnerChunk Result
>(Cell>); - - impl AsChunk for InnerChunk - where - F: FnOnce(&Lua) -> Result
, - { - fn environment(&self, lua: &Lua) -> Result> { - if #caps_len > 0 { - if let Some(make_env) = self.0.take() { - return make_env(lua).map(Some); - } - } - Ok(None) - } - - fn mode(&self) -> Option { - Some(ChunkMode::Text) - } - - fn source<'a>(&self) -> IoResult> { - Ok(Cow::Borrowed((#source).as_bytes())) - } - } - - let make_env = move |lua: &Lua| -> Result
{ - let globals = lua.globals(); - let env = lua.create_table()?; - let meta = lua.create_table()?; - meta.raw_set("__index", &globals)?; - meta.raw_set("__newindex", &globals)?; - - // Add captured variables - #(#caps)* - - env.set_metatable(Some(meta))?; - Ok(env) - }; - - InnerChunk(Cell::new(Some(make_env))) - }}; - - wrapped_code.into() + Chunk::new(input).expand().into() } #[cfg(feature = "macros")] @@ -162,18 +44,12 @@ pub fn userdata(attr: TokenStream, item: TokenStream) -> TokenStream { #[cfg(feature = "macros")] #[proc_macro_attribute] pub fn userdata_impl(attr: TokenStream, item: TokenStream) -> TokenStream { - userdata_impl::userdata_impl(attr, item) + userdata::userdata_impl::userdata_impl(attr, item) } -#[cfg(feature = "macros")] -mod attr; #[cfg(feature = "macros")] mod chunk; #[cfg(feature = "macros")] mod from_lua; #[cfg(feature = "macros")] -mod token; -#[cfg(feature = "macros")] mod userdata; -#[cfg(feature = "macros")] -mod userdata_impl; diff --git a/mlua_derive/src/module.rs b/mlua_derive/src/module.rs new file mode 100644 index 0000000..bad7bf0 --- /dev/null +++ b/mlua_derive/src/module.rs @@ -0,0 +1,68 @@ +use proc_macro::TokenStream; +use proc_macro2::{Ident, Span}; +use quote::quote; +use syn::meta::ParseNestedMeta; +use syn::{ItemFn, LitStr, Result, parse_macro_input}; + +#[derive(Default)] +struct ModuleAttributes { + name: Option, + skip_memory_check: bool, +} + +impl ModuleAttributes { + fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> { + if meta.path.is_ident("name") { + match meta.value() { + Ok(value) => { + self.name = Some(value.parse::()?.parse()?); + } + Err(_) => { + return Err(meta.error("`name` attribute must have a value")); + } + } + } else if meta.path.is_ident("skip_memory_check") { + if meta.value().is_ok() { + return Err(meta.error("`skip_memory_check` attribute have no values")); + } + self.skip_memory_check = true; + } else { + return Err(meta.error("unsupported module attribute")); + } + Ok(()) + } +} + +pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream { + let mut args = ModuleAttributes::default(); + if !attr.is_empty() { + let args_parser = syn::meta::parser(|meta| args.parse(meta)); + parse_macro_input!(attr with args_parser); + } + + let func = parse_macro_input!(item as ItemFn); + let func_name = &func.sig.ident; + let module_name = args.name.unwrap_or_else(|| func_name.clone()); + let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site()); + let skip_memory_check = if args.skip_memory_check { + quote! { lua.skip_memory_check(true); } + } else { + quote! {} + }; + + let wrapped = quote! { + mlua::require_module_feature!(); + + #func + + #[unsafe(no_mangle)] + unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> ::std::os::raw::c_int { + mlua::Lua::entrypoint1(state, move |lua| { + #skip_memory_check + #func_name(lua) + }) + } + }; + + wrapped.into() +} diff --git a/mlua_derive/src/attr.rs b/mlua_derive/src/userdata/attr.rs similarity index 100% rename from mlua_derive/src/attr.rs rename to mlua_derive/src/userdata/attr.rs diff --git a/mlua_derive/src/userdata.rs b/mlua_derive/src/userdata/mod.rs similarity index 98% rename from mlua_derive/src/userdata.rs rename to mlua_derive/src/userdata/mod.rs index 393f3b6..0e578e0 100644 --- a/mlua_derive/src/userdata.rs +++ b/mlua_derive/src/userdata/mod.rs @@ -1,8 +1,11 @@ +mod attr; +pub(crate) mod userdata_impl; + use proc_macro::TokenStream; use quote::{format_ident, quote}; use syn::{Attribute, Data, DeriveInput, Error, Fields, FieldsNamed, Meta, parse_macro_input}; -use crate::attr::LuaAttr; +use self::attr::LuaAttr; /// Parse all `#[lua(...)]` attributes on a field, merging them into one `LuaAttr`. fn parse_field_lua_attr(attrs: &[Attribute]) -> syn::Result { diff --git a/mlua_derive/src/userdata_impl.rs b/mlua_derive/src/userdata/userdata_impl.rs similarity index 99% rename from mlua_derive/src/userdata_impl.rs rename to mlua_derive/src/userdata/userdata_impl.rs index c640378..ca299f1 100644 --- a/mlua_derive/src/userdata_impl.rs +++ b/mlua_derive/src/userdata/userdata_impl.rs @@ -7,7 +7,7 @@ use syn::{ Attribute, FnArg, Ident, ImplItem, ItemImpl, Meta, Signature, Type, parse_macro_input, parse_quote, }; -use crate::attr::LuaAttr; +use super::attr::LuaAttr; /// `&T` reference types that mlua provides as wrapper types via `FromLua`. static BORROW_WRAPPERS: &[(&str, &str)] = &[