1use std::{any::type_name, borrow::Cow, collections::BTreeMap};
2
3use super::{generator::FunctionBuilder, Field, Func, Typed, TypedMultiValue};
4use crate::{
5 extras::{Module, ModuleFields, ModuleMethods},
6 MaybeSend,
7};
8use mlua::{FromLuaMulti, IntoLua, IntoLuaMulti};
9
10#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
12pub struct TypedModuleBuilder {
13 pub doc: Option<Cow<'static, str>>,
14
15 pub nested_modules: BTreeMap<Cow<'static, str>, TypedModuleBuilder>,
16
17 pub fields: BTreeMap<Cow<'static, str>, Field>,
18 pub meta_fields: BTreeMap<Cow<'static, str>, Field>,
19
20 pub functions: BTreeMap<Cow<'static, str>, Func>,
21 pub methods: BTreeMap<Cow<'static, str>, Func>,
22 pub meta_functions: BTreeMap<Cow<'static, str>, Func>,
23 pub meta_methods: BTreeMap<Cow<'static, str>, Func>,
24
25 queued_doc: Option<String>,
26 parents: Vec<&'static str>,
27}
28
29impl TypedModuleBuilder {
30 pub fn new<M: TypedModule>() -> mlua::Result<Self> {
31 let mut builder = TypedModuleBuilder::default();
32
33 if let Some(doc) = M::documentation() {
34 builder.doc = Some(doc.into());
35 }
36
37 M::add_fields(&mut builder)?;
38 M::add_methods(&mut builder)?;
39
40 Ok(builder)
41 }
42
43 #[inline]
44 pub fn is_empty(&self) -> bool {
45 self.fields.is_empty()
46 && self.nested_modules.is_empty()
47 && self.functions.is_empty()
48 && self.methods.is_empty()
49 && self.is_meta_empty()
50 }
51
52 #[inline]
53 pub fn is_meta_empty(&self) -> bool {
54 self.meta_fields.is_empty()
55 && self.meta_functions.is_empty()
56 && self.meta_methods.is_empty()
57 }
58}
59
60pub trait TypedModuleFields<'lua> {
62 fn document<V: AsRef<str>>(&mut self, doc: V) -> &mut Self;
64
65 fn add_field<K, V>(&mut self, name: K, value: V) -> mlua::Result<()>
67 where
68 K: AsRef<str>,
69 V: IntoLua<'lua> + Typed;
70
71 fn add_meta_field<K, V>(&mut self, name: K, value: V) -> mlua::Result<()>
73 where
74 K: AsRef<str>,
75 V: IntoLua<'lua> + Typed;
76
77 fn add_module<V>(&mut self, name: impl AsRef<str>) -> mlua::Result<()>
79 where
80 V: TypedModule;
81}
82
83pub trait TypedModuleMethods<'lua> {
85 fn document<V: AsRef<str>>(&mut self, doc: V) -> &mut Self;
87
88 fn add_function<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
90 where
91 K: AsRef<str>,
92 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
93 A: FromLuaMulti<'lua> + TypedMultiValue,
94 R: IntoLuaMulti<'lua> + TypedMultiValue;
95
96 fn add_function_with<K, F, A, R, G>(
101 &mut self,
102 name: K,
103 function: F,
104 generator: G,
105 ) -> mlua::Result<()>
106 where
107 K: AsRef<str>,
108 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
109 A: FromLuaMulti<'lua> + TypedMultiValue,
110 R: IntoLuaMulti<'lua> + TypedMultiValue,
111 G: Fn(&mut FunctionBuilder<A, R>);
112
113 fn add_meta_function<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
115 where
116 K: AsRef<str>,
117 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
118 A: FromLuaMulti<'lua> + TypedMultiValue,
119 R: IntoLuaMulti<'lua> + TypedMultiValue;
120
121 fn add_meta_function_with<K, F, A, R, G>(
126 &mut self,
127 name: K,
128 function: F,
129 generator: G,
130 ) -> mlua::Result<()>
131 where
132 K: AsRef<str>,
133 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
134 A: FromLuaMulti<'lua> + TypedMultiValue,
135 R: IntoLuaMulti<'lua> + TypedMultiValue,
136 G: Fn(&mut FunctionBuilder<A, R>);
137
138 fn add_method<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
140 where
141 K: AsRef<str>,
142 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
143 A: FromLuaMulti<'lua> + TypedMultiValue,
144 R: IntoLuaMulti<'lua> + TypedMultiValue;
145
146 fn add_method_with<K, F, A, R, G>(
151 &mut self,
152 name: K,
153 function: F,
154 generator: G,
155 ) -> mlua::Result<()>
156 where
157 K: AsRef<str>,
158 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
159 A: FromLuaMulti<'lua> + TypedMultiValue,
160 R: IntoLuaMulti<'lua> + TypedMultiValue,
161 G: Fn(&mut FunctionBuilder<A, R>);
162
163 fn add_meta_method<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
165 where
166 K: AsRef<str>,
167 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
168 A: FromLuaMulti<'lua> + TypedMultiValue,
169 R: IntoLuaMulti<'lua> + TypedMultiValue;
170
171 fn add_meta_method_with<K, F, A, R, G>(
176 &mut self,
177 name: K,
178 function: F,
179 generator: G,
180 ) -> mlua::Result<()>
181 where
182 K: AsRef<str>,
183 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
184 A: FromLuaMulti<'lua> + TypedMultiValue,
185 R: IntoLuaMulti<'lua> + TypedMultiValue,
186 G: Fn(&mut FunctionBuilder<A, R>);
187}
188
189pub struct WrappedModule<'module, M>(pub &'module mut M);
190impl<'module, 'lua, M: ModuleFields<'lua>> TypedModuleFields<'lua> for WrappedModule<'module, M> {
191 fn document<V: AsRef<str>>(&mut self, _doc: V) -> &mut Self {
192 self
193 }
194
195 fn add_field<K, V>(&mut self, name: K, value: V) -> mlua::Result<()>
196 where
197 K: AsRef<str>,
198 V: IntoLua<'lua> + Typed,
199 {
200 self.0.add_field(name.as_ref(), value)
201 }
202
203 fn add_meta_field<K, V>(&mut self, name: K, value: V) -> mlua::Result<()>
204 where
205 K: AsRef<str>,
206 V: IntoLua<'lua> + Typed,
207 {
208 self.0.add_meta_field(name.as_ref(), value)
209 }
210
211 fn add_module<V>(&mut self, name: impl AsRef<str>) -> mlua::Result<()>
212 where
213 V: TypedModule,
214 {
215 self.0.add_module::<&str, V>(name.as_ref())
216 }
217}
218
219impl<'module, 'lua, M: ModuleMethods<'lua>> TypedModuleMethods<'lua> for WrappedModule<'module, M> {
220 fn document<V: AsRef<str>>(&mut self, _doc: V) -> &mut Self {
221 self
222 }
223
224 fn add_function<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
225 where
226 K: AsRef<str>,
227 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
228 A: FromLuaMulti<'lua> + TypedMultiValue,
229 R: IntoLuaMulti<'lua> + TypedMultiValue,
230 {
231 self.0
232 .add_function::<&str, F, A, R>(name.as_ref(), function)
233 }
234
235 fn add_function_with<K, F, A, R, G>(
236 &mut self,
237 name: K,
238 function: F,
239 _generator: G,
240 ) -> mlua::Result<()>
241 where
242 K: AsRef<str>,
243 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
244 A: FromLuaMulti<'lua> + TypedMultiValue,
245 R: IntoLuaMulti<'lua> + TypedMultiValue,
246 G: Fn(&mut FunctionBuilder<A, R>),
247 {
248 self.0
249 .add_function::<&str, F, A, R>(name.as_ref(), function)
250 }
251
252 fn add_meta_function<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
253 where
254 K: AsRef<str>,
255 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
256 A: FromLuaMulti<'lua> + TypedMultiValue,
257 R: IntoLuaMulti<'lua> + TypedMultiValue,
258 {
259 self.0.add_meta_function::<&str, F, A, R>(name.as_ref(), function)
260 }
261
262 fn add_meta_function_with<K, F, A, R, G>(
263 &mut self,
264 name: K,
265 function: F,
266 _generator: G,
267 ) -> mlua::Result<()>
268 where
269 K: AsRef<str>,
270 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
271 A: FromLuaMulti<'lua> + TypedMultiValue,
272 R: IntoLuaMulti<'lua> + TypedMultiValue,
273 G: Fn(&mut FunctionBuilder<A, R>),
274 {
275 self.0.add_meta_function::<&str, F, A, R>(name.as_ref(), function)
276 }
277
278 fn add_method<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
279 where
280 K: AsRef<str>,
281 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
282 A: FromLuaMulti<'lua> + TypedMultiValue,
283 R: IntoLuaMulti<'lua> + TypedMultiValue,
284 {
285 self.0
286 .add_method::<&str, F, A, R>(name.as_ref(), function)
287 }
288
289 fn add_method_with<K, F, A, R, G>(
290 &mut self,
291 name: K,
292 function: F,
293 _generator: G,
294 ) -> mlua::Result<()>
295 where
296 K: AsRef<str>,
297 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
298 A: FromLuaMulti<'lua> + TypedMultiValue,
299 R: IntoLuaMulti<'lua> + TypedMultiValue,
300 G: Fn(&mut FunctionBuilder<A, R>),
301 {
302 self.0
303 .add_method::<&str, F, A, R>(name.as_ref(), function)
304 }
305
306 fn add_meta_method<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
307 where
308 K: AsRef<str>,
309 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
310 A: FromLuaMulti<'lua> + TypedMultiValue,
311 R: IntoLuaMulti<'lua> + TypedMultiValue,
312 {
313 self.0.add_meta_method::<&str, F, A, R>(name.as_ref(), function)
314 }
315
316 fn add_meta_method_with<K, F, A, R, G>(
317 &mut self,
318 name: K,
319 function: F,
320 _generator: G,
321 ) -> mlua::Result<()>
322 where
323 K: AsRef<str>,
324 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
325 A: FromLuaMulti<'lua> + TypedMultiValue,
326 R: IntoLuaMulti<'lua> + TypedMultiValue,
327 R: IntoLuaMulti<'lua> + TypedMultiValue,
328 {
329 self.0
330 .add_meta_method::<&str, F, A, R>(name.as_ref(), function)
331 }
332}
333
334impl<'lua> TypedModuleFields<'lua> for TypedModuleBuilder {
335 fn document<V: AsRef<str>>(&mut self, doc: V) -> &mut Self {
336 self.queued_doc = Some(doc.as_ref().into());
337 self
338 }
339
340 fn add_module<V>(&mut self, name: impl AsRef<str>) -> mlua::Result<()>
341 where
342 V: TypedModule,
343 {
344 if self.parents.contains(&type_name::<V>()) {
345 return Err(mlua::Error::runtime(format!(
346 "infinite nested modules using: '{}'",
347 type_name::<V>()
348 )));
349 }
350
351 let mut nested = TypedModuleBuilder {
352 parents: self
353 .parents
354 .iter()
355 .map(|v| *v)
356 .chain([type_name::<V>()])
357 .collect(),
358 ..Default::default()
359 };
360
361 if let Some(doc) = V::documentation() {
362 nested.doc = Some(doc.into());
363 }
364
365 V::add_fields(&mut nested)?;
366 V::add_methods(&mut nested)?;
367
368 self.nested_modules.insert(name.as_ref().to_string().into(), nested);
369 Ok(())
370 }
371
372 fn add_field<K, V>(&mut self, name: K, _value: V) -> mlua::Result<()>
373 where
374 K: AsRef<str>,
375 V: IntoLua<'lua> + Typed,
376 {
377 self.fields.insert(
378 name.as_ref().to_string().into(),
379 Field {
380 ty: V::ty(),
381 doc: self.queued_doc.take().map(|v| v.into()),
382 },
383 );
384 Ok(())
385 }
386
387 fn add_meta_field<K, V>(&mut self, name: K, _value: V) -> mlua::Result<()>
388 where
389 K: AsRef<str>,
390 V: IntoLua<'lua> + Typed,
391 {
392 self.meta_fields.insert(
393 name.as_ref().to_string().into(),
394 Field {
395 ty: V::ty(),
396 doc: self.queued_doc.take().map(|v| v.into()),
397 },
398 );
399 Ok(())
400 }
401}
402
403impl<'lua> TypedModuleMethods<'lua> for TypedModuleBuilder {
404 fn document<V: AsRef<str>>(&mut self, doc: V) -> &mut Self {
405 self.queued_doc = Some(doc.as_ref().into());
406 self
407 }
408
409 fn add_function<K, F, A, R>(&mut self, name: K, _function: F) -> mlua::Result<()>
410 where
411 K: AsRef<str>,
412 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
413 A: FromLuaMulti<'lua> + TypedMultiValue,
414 R: IntoLuaMulti<'lua> + TypedMultiValue,
415 {
416 self.functions.insert(
417 name.as_ref().to_string().into(),
418 Func {
419 params: A::get_types_as_params(),
420 returns: R::get_types_as_returns(),
421 doc: self.queued_doc.take().map(|v| v.into()),
422 },
423 );
424 Ok(())
425 }
426
427 fn add_function_with<K, F, A, R, G>(
428 &mut self,
429 name: K,
430 _function: F,
431 generator: G,
432 ) -> mlua::Result<()>
433 where
434 K: AsRef<str>,
435 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
436 A: FromLuaMulti<'lua> + TypedMultiValue,
437 R: IntoLuaMulti<'lua> + TypedMultiValue,
438 G: Fn(&mut FunctionBuilder<A, R>),
439 {
440 let mut builder = FunctionBuilder::<A, R>::default();
441 generator(&mut builder);
442
443 self.functions.insert(
444 name.as_ref().to_string().into(),
445 Func {
446 params: builder.params,
447 returns: builder.returns,
448 doc: self.queued_doc.take().map(|v| v.into()),
449 },
450 );
451 Ok(())
452 }
453
454 fn add_meta_function<K, F, A, R>(&mut self, name: K, _function: F) -> mlua::Result<()>
455 where
456 K: AsRef<str>,
457 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
458 A: FromLuaMulti<'lua> + TypedMultiValue,
459 R: IntoLuaMulti<'lua> + TypedMultiValue,
460 {
461 self.meta_functions.insert(
462 name.as_ref().to_string().into(),
463 Func {
464 params: A::get_types_as_params(),
465 returns: R::get_types_as_returns(),
466 doc: self.queued_doc.take().map(|v| v.into()),
467 },
468 );
469 Ok(())
470 }
471
472 fn add_meta_function_with<K, F, A, R, G>(
473 &mut self,
474 name: K,
475 _function: F,
476 generator: G,
477 ) -> mlua::Result<()>
478 where
479 K: AsRef<str>,
480 F: Fn(&mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
481 A: FromLuaMulti<'lua> + TypedMultiValue,
482 R: IntoLuaMulti<'lua> + TypedMultiValue,
483 G: Fn(&mut FunctionBuilder<A, R>),
484 {
485 let mut builder = FunctionBuilder::<A, R>::default();
486 generator(&mut builder);
487
488 self.meta_functions.insert(
489 name.as_ref().to_string().into(),
490 Func {
491 params: builder.params,
492 returns: builder.returns,
493 doc: self.queued_doc.take().map(|v| v.into()),
494 },
495 );
496 Ok(())
497 }
498
499 fn add_method<K, F, A, R>(&mut self, name: K, _function: F) -> mlua::Result<()>
500 where
501 K: AsRef<str>,
502 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
503 A: FromLuaMulti<'lua> + TypedMultiValue,
504 R: IntoLuaMulti<'lua> + TypedMultiValue,
505 {
506 self.methods.insert(
507 name.as_ref().to_string().into(),
508 Func {
509 params: A::get_types_as_params(),
510 returns: R::get_types_as_returns(),
511 doc: self.queued_doc.take().map(|v| v.into()),
512 },
513 );
514 Ok(())
515 }
516
517 fn add_method_with<K, F, A, R, G>(
518 &mut self,
519 name: K,
520 _function: F,
521 generator: G,
522 ) -> mlua::Result<()>
523 where
524 K: AsRef<str>,
525 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
526 A: FromLuaMulti<'lua> + TypedMultiValue,
527 R: IntoLuaMulti<'lua> + TypedMultiValue,
528 G: Fn(&mut FunctionBuilder<A, R>),
529 {
530 let mut builder = FunctionBuilder::<A, R>::default();
531 generator(&mut builder);
532
533 self.methods.insert(
534 name.as_ref().to_string().into(),
535 Func {
536 params: builder.params,
537 returns: builder.returns,
538 doc: self.queued_doc.take().map(|v| v.into()),
539 },
540 );
541 Ok(())
542 }
543
544 fn add_meta_method<K, F, A, R>(&mut self, name: K, _function: F) -> mlua::Result<()>
545 where
546 K: AsRef<str>,
547 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
548 A: FromLuaMulti<'lua> + TypedMultiValue,
549 R: IntoLuaMulti<'lua> + TypedMultiValue,
550 {
551 self.meta_methods.insert(
552 name.as_ref().to_string().into(),
553 Func {
554 params: A::get_types_as_params(),
555 returns: R::get_types_as_returns(),
556 doc: self.queued_doc.take().map(|v| v.into()),
557 },
558 );
559 Ok(())
560 }
561
562 fn add_meta_method_with<K, F, A, R, G>(
563 &mut self,
564 name: K,
565 _function: F,
566 generator: G,
567 ) -> mlua::Result<()>
568 where
569 K: AsRef<str>,
570 F: Fn(&mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
571 A: FromLuaMulti<'lua> + TypedMultiValue,
572 R: IntoLuaMulti<'lua> + TypedMultiValue,
573 G: Fn(&mut FunctionBuilder<A, R>),
574 {
575 let mut builder = FunctionBuilder::<A, R>::default();
576 generator(&mut builder);
577
578 self.meta_methods.insert(
579 name.as_ref().to_string().into(),
580 Func {
581 params: builder.params,
582 returns: builder.returns,
583 doc: self.queued_doc.take().map(|v| v.into()),
584 },
585 );
586 Ok(())
587 }
588}
589
590pub trait TypedModule: Sized {
594 #[inline]
596 fn documentation() -> Option<String> { None }
597
598 #[allow(unused_variables)]
600 fn add_fields<'lua, F: TypedModuleFields<'lua>>(fields: &mut F) -> mlua::Result<()> {
601 Ok(())
602 }
603
604 #[allow(unused_variables)]
606 fn add_methods<'lua, M: TypedModuleMethods<'lua>>(methods: &mut M) -> mlua::Result<()> {
607 Ok(())
608 }
609}
610
611impl<T: TypedModule> Module for T {
612 fn add_fields<'lua, F: ModuleFields<'lua>>(fields: &mut F) -> mlua::Result<()> {
613 let mut wrapped = WrappedModule(fields);
614 T::add_fields(&mut wrapped)
615 }
616
617 fn add_methods<'lua, M: ModuleMethods<'lua>>(methods: &mut M) -> mlua::Result<()> {
618 let mut wrapped = WrappedModule(methods);
619 T::add_methods(&mut wrapped)
620 }
621}