Skip to content

Commit 3c34663

Browse files
committed
mlua_derive: Group functionality in modules
Create new `chunk`, `userdata`, `module` modules.
1 parent f8b308c commit 3c34663

8 files changed

Lines changed: 151 additions & 149 deletions

File tree

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ use std::ops::Deref;
22

33
use proc_macro::TokenStream;
44
use proc_macro2::TokenStream as TokenStream2;
5-
use quote::ToTokens;
5+
use quote::{ToTokens, quote};
66

7-
use crate::token::{Pos, Token, Tokens};
7+
use self::token::{Pos, Token, Tokens};
8+
9+
mod token;
810

911
#[derive(Debug, Clone)]
1012
pub(crate) struct Capture(Token);
@@ -95,11 +97,64 @@ impl Chunk {
9597
}
9698
}
9799

98-
pub(crate) fn source(&self) -> &str {
99-
&self.source
100-
}
101-
102100
pub(crate) fn captures(&self) -> &[Capture] {
103101
self.caps.captures()
104102
}
103+
104+
pub(crate) fn expand(&self) -> TokenStream2 {
105+
let source = &self.source;
106+
107+
let caps_len = self.captures().len();
108+
let caps = self.captures().iter().map(|cap| {
109+
let cap_name = cap.name();
110+
quote! { env.raw_set(#cap_name, #cap)?; }
111+
});
112+
113+
quote! {{
114+
use mlua::{AsChunk, ChunkMode, Lua, Result, Table};
115+
use ::std::borrow::Cow;
116+
use ::std::cell::Cell;
117+
use ::std::io::Result as IoResult;
118+
119+
struct InnerChunk<F: FnOnce(&Lua) -> Result<Table>>(Cell<Option<F>>);
120+
121+
impl<F> AsChunk for InnerChunk<F>
122+
where
123+
F: FnOnce(&Lua) -> Result<Table>,
124+
{
125+
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
126+
if #caps_len > 0 {
127+
if let Some(make_env) = self.0.take() {
128+
return make_env(lua).map(Some);
129+
}
130+
}
131+
Ok(None)
132+
}
133+
134+
fn mode(&self) -> Option<ChunkMode> {
135+
Some(ChunkMode::Text)
136+
}
137+
138+
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
139+
Ok(Cow::Borrowed((#source).as_bytes()))
140+
}
141+
}
142+
143+
let make_env = move |lua: &Lua| -> Result<Table> {
144+
let globals = lua.globals();
145+
let env = lua.create_table()?;
146+
let meta = lua.create_table()?;
147+
meta.raw_set("__index", &globals)?;
148+
meta.raw_set("__newindex", &globals)?;
149+
150+
// Add captured variables
151+
#(#caps)*
152+
153+
env.set_metatable(Some(meta))?;
154+
Ok(env)
155+
};
156+
157+
InnerChunk(Cell::new(Some(make_env)))
158+
}}
159+
}
105160
}

mlua_derive/src/from_lua.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@ pub fn from_lua(input: TokenStream) -> TokenStream {
1313
};
1414

1515
quote! {
16-
impl #impl_generics ::mlua::FromLua for #ident #ty_generics #where_clause {
17-
#[inline]
18-
fn from_lua(value: ::mlua::Value, _: &::mlua::Lua) -> ::mlua::Result<Self> {
19-
match value {
20-
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
21-
_ => Err(::mlua::Error::FromLuaConversionError {
22-
from: value.type_name(),
23-
to: #ident_str.to_string(),
24-
message: None,
25-
}),
26-
}
16+
impl #impl_generics ::mlua::FromLua for #ident #ty_generics #where_clause {
17+
#[inline]
18+
fn from_lua(value: ::mlua::Value, _: &::mlua::Lua) -> ::mlua::Result<Self> {
19+
match value {
20+
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
21+
_ => Err(::mlua::Error::FromLuaConversionError {
22+
from: value.type_name(),
23+
to: #ident_str.to_string(),
24+
message: None,
25+
}),
26+
}
27+
}
2728
}
28-
}
2929
}
3030
.into()
3131
}

mlua_derive/src/lib.rs

Lines changed: 5 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use proc_macro::TokenStream;
2-
use proc_macro2::{Ident, Span};
3-
use quote::quote;
4-
use syn::meta::ParseNestedMeta;
5-
use syn::{ItemFn, LitStr, Result, parse_macro_input};
2+
3+
mod module;
64

75
#[cfg(feature = "macros")]
86
use {crate::chunk::Chunk, proc_macro_error2::proc_macro_error};
@@ -17,132 +15,16 @@ macro_rules! try_compile {
1715
};
1816
}
1917

20-
#[derive(Default)]
21-
struct ModuleAttributes {
22-
name: Option<Ident>,
23-
skip_memory_check: bool,
24-
}
25-
26-
impl ModuleAttributes {
27-
fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
28-
if meta.path.is_ident("name") {
29-
match meta.value() {
30-
Ok(value) => {
31-
self.name = Some(value.parse::<LitStr>()?.parse()?);
32-
}
33-
Err(_) => {
34-
return Err(meta.error("`name` attribute must have a value"));
35-
}
36-
}
37-
} else if meta.path.is_ident("skip_memory_check") {
38-
if meta.value().is_ok() {
39-
return Err(meta.error("`skip_memory_check` attribute have no values"));
40-
}
41-
self.skip_memory_check = true;
42-
} else {
43-
return Err(meta.error("unsupported module attribute"));
44-
}
45-
Ok(())
46-
}
47-
}
48-
4918
#[proc_macro_attribute]
5019
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
51-
let mut args = ModuleAttributes::default();
52-
if !attr.is_empty() {
53-
let args_parser = syn::meta::parser(|meta| args.parse(meta));
54-
parse_macro_input!(attr with args_parser);
55-
}
56-
57-
let func = parse_macro_input!(item as ItemFn);
58-
let func_name = &func.sig.ident;
59-
let module_name = args.name.unwrap_or_else(|| func_name.clone());
60-
let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site());
61-
let skip_memory_check = if args.skip_memory_check {
62-
quote! { lua.skip_memory_check(true); }
63-
} else {
64-
quote! {}
65-
};
66-
67-
let wrapped = quote! {
68-
mlua::require_module_feature!();
69-
70-
#func
71-
72-
#[unsafe(no_mangle)]
73-
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> ::std::os::raw::c_int {
74-
mlua::Lua::entrypoint1(state, move |lua| {
75-
#skip_memory_check
76-
#func_name(lua)
77-
})
78-
}
79-
};
80-
81-
wrapped.into()
20+
module::lua_module(attr, item)
8221
}
8322

8423
#[cfg(feature = "macros")]
8524
#[proc_macro]
8625
#[proc_macro_error]
8726
pub fn chunk(input: TokenStream) -> TokenStream {
88-
let chunk = Chunk::new(input);
89-
90-
let source = chunk.source();
91-
92-
let caps_len = chunk.captures().len();
93-
let caps = chunk.captures().iter().map(|cap| {
94-
let cap_name = cap.name();
95-
quote! { env.raw_set(#cap_name, #cap)?; }
96-
});
97-
98-
let wrapped_code = quote! {{
99-
use mlua::{AsChunk, ChunkMode, Lua, Result, Table};
100-
use ::std::borrow::Cow;
101-
use ::std::cell::Cell;
102-
use ::std::io::Result as IoResult;
103-
104-
struct InnerChunk<F: FnOnce(&Lua) -> Result<Table>>(Cell<Option<F>>);
105-
106-
impl<F> AsChunk for InnerChunk<F>
107-
where
108-
F: FnOnce(&Lua) -> Result<Table>,
109-
{
110-
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
111-
if #caps_len > 0 {
112-
if let Some(make_env) = self.0.take() {
113-
return make_env(lua).map(Some);
114-
}
115-
}
116-
Ok(None)
117-
}
118-
119-
fn mode(&self) -> Option<ChunkMode> {
120-
Some(ChunkMode::Text)
121-
}
122-
123-
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
124-
Ok(Cow::Borrowed((#source).as_bytes()))
125-
}
126-
}
127-
128-
let make_env = move |lua: &Lua| -> Result<Table> {
129-
let globals = lua.globals();
130-
let env = lua.create_table()?;
131-
let meta = lua.create_table()?;
132-
meta.raw_set("__index", &globals)?;
133-
meta.raw_set("__newindex", &globals)?;
134-
135-
// Add captured variables
136-
#(#caps)*
137-
138-
env.set_metatable(Some(meta))?;
139-
Ok(env)
140-
};
141-
142-
InnerChunk(Cell::new(Some(make_env)))
143-
}};
144-
145-
wrapped_code.into()
27+
Chunk::new(input).expand().into()
14628
}
14729

14830
#[cfg(feature = "macros")]
@@ -162,18 +44,12 @@ pub fn userdata(attr: TokenStream, item: TokenStream) -> TokenStream {
16244
#[cfg(feature = "macros")]
16345
#[proc_macro_attribute]
16446
pub fn userdata_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
165-
userdata_impl::userdata_impl(attr, item)
47+
userdata::userdata_impl::userdata_impl(attr, item)
16648
}
16749

168-
#[cfg(feature = "macros")]
169-
mod attr;
17050
#[cfg(feature = "macros")]
17151
mod chunk;
17252
#[cfg(feature = "macros")]
17353
mod from_lua;
17454
#[cfg(feature = "macros")]
175-
mod token;
176-
#[cfg(feature = "macros")]
17755
mod userdata;
178-
#[cfg(feature = "macros")]
179-
mod userdata_impl;

mlua_derive/src/module.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use proc_macro::TokenStream;
2+
use proc_macro2::{Ident, Span};
3+
use quote::quote;
4+
use syn::meta::ParseNestedMeta;
5+
use syn::{ItemFn, LitStr, Result, parse_macro_input};
6+
7+
#[derive(Default)]
8+
struct ModuleAttributes {
9+
name: Option<Ident>,
10+
skip_memory_check: bool,
11+
}
12+
13+
impl ModuleAttributes {
14+
fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
15+
if meta.path.is_ident("name") {
16+
match meta.value() {
17+
Ok(value) => {
18+
self.name = Some(value.parse::<LitStr>()?.parse()?);
19+
}
20+
Err(_) => {
21+
return Err(meta.error("`name` attribute must have a value"));
22+
}
23+
}
24+
} else if meta.path.is_ident("skip_memory_check") {
25+
if meta.value().is_ok() {
26+
return Err(meta.error("`skip_memory_check` attribute have no values"));
27+
}
28+
self.skip_memory_check = true;
29+
} else {
30+
return Err(meta.error("unsupported module attribute"));
31+
}
32+
Ok(())
33+
}
34+
}
35+
36+
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
37+
let mut args = ModuleAttributes::default();
38+
if !attr.is_empty() {
39+
let args_parser = syn::meta::parser(|meta| args.parse(meta));
40+
parse_macro_input!(attr with args_parser);
41+
}
42+
43+
let func = parse_macro_input!(item as ItemFn);
44+
let func_name = &func.sig.ident;
45+
let module_name = args.name.unwrap_or_else(|| func_name.clone());
46+
let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site());
47+
let skip_memory_check = if args.skip_memory_check {
48+
quote! { lua.skip_memory_check(true); }
49+
} else {
50+
quote! {}
51+
};
52+
53+
let wrapped = quote! {
54+
mlua::require_module_feature!();
55+
56+
#func
57+
58+
#[unsafe(no_mangle)]
59+
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> ::std::os::raw::c_int {
60+
mlua::Lua::entrypoint1(state, move |lua| {
61+
#skip_memory_check
62+
#func_name(lua)
63+
})
64+
}
65+
};
66+
67+
wrapped.into()
68+
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
mod attr;
2+
pub(crate) mod userdata_impl;
3+
14
use proc_macro::TokenStream;
25
use quote::{format_ident, quote};
36
use syn::{Attribute, Data, DeriveInput, Error, Fields, FieldsNamed, Meta, parse_macro_input};
47

5-
use crate::attr::LuaAttr;
8+
use self::attr::LuaAttr;
69

710
/// Parse all `#[lua(...)]` attributes on a field, merging them into one `LuaAttr`.
811
fn parse_field_lua_attr(attrs: &[Attribute]) -> syn::Result<LuaAttr> {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use syn::{
77
Attribute, FnArg, Ident, ImplItem, ItemImpl, Meta, Signature, Type, parse_macro_input, parse_quote,
88
};
99

10-
use crate::attr::LuaAttr;
10+
use super::attr::LuaAttr;
1111

1212
/// `&T` reference types that mlua provides as wrapper types via `FromLua`.
1313
static BORROW_WRAPPERS: &[(&str, &str)] = &[

0 commit comments

Comments
 (0)