1use std::{any::type_name, marker::PhantomData};
2
3use mlua::{FromLuaMulti, IntoLua, IntoLuaMulti};
4
5use crate::MaybeSend;
6
7#[derive(Default)]
8pub struct LuaModule<M>(PhantomData<M>);
9impl<'lua, M: Module> IntoLua<'lua> for LuaModule<M> {
10 fn into_lua(
11 self,
12 lua: &'lua mlua::prelude::Lua,
13 ) -> mlua::prelude::LuaResult<mlua::prelude::LuaValue<'lua>> {
14 let mut builder: ModuleBuilder<'lua> = ModuleBuilder {
15 table: lua.create_table()?,
16 lua,
17 parents: Vec::new(),
18 };
19
20 M::add_fields(&mut builder)?;
21 M::add_methods(&mut builder)?;
22
23 Ok(mlua::Value::Table(builder.table))
24 }
25}
26
27pub trait Module: Sized {
29 #[allow(unused_variables)]
31 fn add_fields<'lua, F: ModuleFields<'lua>>(fields: &mut F) -> mlua::Result<()> {
32 Ok(())
33 }
34
35 #[allow(unused_variables)]
37 fn add_methods<'lua, M: ModuleMethods<'lua>>(methods: &mut M) -> mlua::Result<()> {
38 Ok(())
39 }
40
41 fn module() -> LuaModule<Self> {
42 LuaModule(PhantomData)
43 }
44}
45
46pub trait ModuleFields<'lua> {
48 fn add_field<K, V>(&mut self, name: K, value: V) -> mlua::Result<()>
50 where
51 K: IntoLua<'lua>,
52 V: IntoLua<'lua>;
53
54 fn add_meta_field<K, V>(&mut self, name: K, value: V) -> mlua::Result<()>
56 where
57 K: IntoLua<'lua>,
58 V: IntoLua<'lua>;
59
60 fn add_module<K, V>(&mut self, name: K) -> mlua::Result<()>
62 where
63 K: IntoLua<'lua>,
64 V: Module;
65}
66
67pub trait ModuleMethods<'lua> {
69 fn add_function<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
71 where
72 K: IntoLua<'lua>,
73 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
74 A: FromLuaMulti<'lua>,
75 R: IntoLuaMulti<'lua>;
76
77 fn add_meta_function<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
79 where
80 K: IntoLua<'lua>,
81 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
82 A: FromLuaMulti<'lua>,
83 R: IntoLuaMulti<'lua>;
84
85 fn add_method<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
87 where
88 K: IntoLua<'lua>,
89 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
90 A: FromLuaMulti<'lua>,
91 R: IntoLuaMulti<'lua>;
92
93 fn add_meta_method<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
95 where
96 K: IntoLua<'lua>,
97 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
98 A: FromLuaMulti<'lua>,
99 R: IntoLuaMulti<'lua>;
100}
101
102pub struct ModuleBuilder<'lua> {
104 lua: &'lua mlua::Lua,
105 table: mlua::Table<'lua>,
106 parents: Vec<&'static str>,
107}
108
109impl<'lua> ModuleFields<'lua> for ModuleBuilder<'lua> {
110 fn add_field<K, V>(&mut self, name: K, value: V) -> mlua::Result<()>
111 where
112 K: IntoLua<'lua>,
113 V: IntoLua<'lua>,
114 {
115 self.table.set(name, value)
116 }
117
118 fn add_meta_field<K, V>(&mut self, name: K, value: V) -> mlua::Result<()>
119 where
120 K: IntoLua<'lua>,
121 V: IntoLua<'lua>,
122 {
123 let meta = match self.table.get_metatable() {
124 Some(meta) => meta,
125 None => {
126 let meta = self.lua.create_table()?;
127 self.table.set_metatable(Some(meta.clone()));
128 meta
129 }
130 };
131
132 meta.set(name, value)
133 }
134
135 fn add_module<K, V>(&mut self, name: K) -> mlua::Result<()>
136 where
137 K: IntoLua<'lua>,
138 V: Module,
139 {
140 if self.parents.contains(&type_name::<V>()) {
141 return Err(mlua::Error::runtime(format!(
142 "infinite nested modules using: '{}'",
143 type_name::<V>()
144 )));
145 }
146
147 let mut builder: ModuleBuilder<'lua> = ModuleBuilder {
148 table: self.lua.create_table()?,
149 lua: self.lua,
150 parents: self
151 .parents
152 .iter()
153 .map(|v| *v)
154 .chain([type_name::<V>()])
155 .collect(),
156 };
157
158 V::add_fields(&mut builder)?;
159 V::add_methods(&mut builder)?;
160
161 self.table.set(name, builder.table)
162 }
163}
164
165impl<'lua> ModuleMethods<'lua> for ModuleBuilder<'lua> {
166 fn add_function<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
167 where
168 K: IntoLua<'lua>,
169 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
170 A: FromLuaMulti<'lua>,
171 R: IntoLuaMulti<'lua>,
172 {
173 self.table.set(name, self.lua.create_function(function)?)
174 }
175
176 fn add_meta_function<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
177 where
178 K: IntoLua<'lua>,
179 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
180 A: FromLuaMulti<'lua>,
181 R: IntoLuaMulti<'lua>,
182 {
183 let meta = match self.table.get_metatable() {
184 Some(meta) => meta,
185 None => {
186 let meta = self.lua.create_table()?;
187 self.table.set_metatable(Some(meta.clone()));
188 meta
189 }
190 };
191
192 meta.set(name, self.lua.create_function(function)?)
193 }
194
195 fn add_method<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
196 where
197 K: IntoLua<'lua>,
198 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
199 A: FromLuaMulti<'lua>,
200 R: IntoLuaMulti<'lua>,
201 {
202 self.table.set(
203 name,
204 self.lua
205 .create_function(move |lua, args: mlua::MultiValue| {
206 let this = mlua::Table::from_lua_multi(args.clone(), lua)?;
207 let rest = A::from_lua_multi(args, lua)?;
208 function(lua, this, rest)
209 })?,
210 )
211 }
212
213 fn add_meta_method<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
214 where
215 K: IntoLua<'lua>,
216 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
217 A: FromLuaMulti<'lua>,
218 R: IntoLuaMulti<'lua>,
219 {
220 let meta = match self.table.get_metatable() {
221 Some(meta) => meta,
222 None => {
223 let meta = self.lua.create_table()?;
224 self.table.set_metatable(Some(meta.clone()));
225 meta
226 }
227 };
228
229 meta.set(
230 name,
231 self.lua
232 .create_function(move |lua, args: mlua::MultiValue| {
233 let this = mlua::Table::from_lua_multi(args.clone(), lua)?;
234 let rest = A::from_lua_multi(args, lua)?;
235 function(lua, this, rest)
236 })?,
237 )
238 }
239}