mlua_extras/typed/
module.rs

1use std::{any::{type_name, Any}, borrow::Cow, collections::BTreeMap};
2
3use super::{generator::FunctionBuilder, Field, Func, Index, IntoDocComment, Type, Typed, TypedMultiValue};
4use crate::{
5    extras::{Module, ModuleFields, ModuleMethods},
6    MaybeSend,
7};
8use mlua::{FromLuaMulti, IntoLua, IntoLuaMulti};
9
10/// Builder that constructs type and documentation information for a module using the [`TypedModule`] trait
11#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct TypedModuleBuilder {
13    pub doc: Option<Cow<'static, str>>,
14
15    pub nested_modules: BTreeMap<Index, TypedModuleBuilder>,
16
17    pub fields: BTreeMap<Index, Field>,
18    pub meta_fields: BTreeMap<Index, Field>,
19
20    pub functions: BTreeMap<Index, Func>,
21    pub methods: BTreeMap<Index, Func>,
22    pub meta_functions: BTreeMap<Index, Func>,
23    pub meta_methods: BTreeMap<Index, Func>,
24
25    queued_doc: Option<String>,
26    parents: Vec<&'static str>,
27}
28
29impl From<TypedModuleBuilder> for Type {
30    fn from(value: TypedModuleBuilder) -> Self {
31        Type::Module(Box::new(value))
32    }
33}
34
35impl TypedModuleBuilder {
36    pub fn new<M: TypedModule>() -> mlua::Result<Self> {
37        let mut builder = TypedModuleBuilder::default();
38
39        if let Some(doc) = M::documentation() {
40            builder.doc = Some(doc.into());
41        }
42
43        M::add_fields(&mut builder)?;
44        M::add_methods(&mut builder)?;
45
46        Ok(builder)
47    }
48
49    #[inline]
50    pub fn is_empty(&self) -> bool {
51        self.fields.is_empty()
52            && self.nested_modules.is_empty()
53            && self.functions.is_empty()
54            && self.methods.is_empty()
55            && self.is_meta_empty()
56    }
57
58    #[inline]
59    pub fn is_meta_empty(&self) -> bool {
60        self.meta_fields.is_empty()
61            && self.meta_functions.is_empty()
62            && self.meta_methods.is_empty()
63    }
64
65    /// Creates a new typed field and adds it to the class's type information
66    ///
67    /// # Example
68    ///
69    /// ```
70    /// use mlua_extras::typed::{TypedModuleBuilder, Type};
71    ///
72    /// static NAME: &str = "mlua_extras";
73    ///
74    /// TypedModuleBuilder::default()
75    ///     .field("data1", Type::string() | Type::nil(), "doc comment goes last")
76    ///     .field("data2", Type::array(Type::string()), ()) // Can also use `None` instead of `()`
77    ///     .field("message", Type::string(), foramt!("A message for {NAME}"))
78    /// ```
79    pub fn field<S: AsRef<str>>(mut self, name: impl AsRef<str>, ty: Type, doc: impl IntoDocComment) -> Self {
80        self.fields.insert(name.as_ref().to_string().into(), Field::new(ty, doc));
81        self
82    }
83
84    /// Creates a new typed function and adds it to the class's type information
85    ///
86    /// # Example
87    ///
88    /// ```
89    /// use mlua_extras::typed::{TypedModuleBuilder, Type};
90    ///
91    /// TypedModuleBuilder::default()
92    ///     .function::<String, ()>("greet", "Greet the given name")
93    ///     // Can use `None` instead of `()` for specifying the doc comment
94    ///     .function::<String, ()>("hello", ())
95    /// ```
96    pub fn function<Params, Returns>(mut self, name: impl AsRef<str>, doc: impl IntoDocComment) -> Self
97    where
98        Params: TypedMultiValue,
99        Returns: TypedMultiValue,
100    {
101        self.functions.insert(name.as_ref().to_string().into(), Func::new::<Params, Returns>(doc));
102        self
103    }
104
105    /// Same as [`function`][TypedModuleBuilder::function] but with an extra generator function
106    /// parameter.
107    ///
108    /// This extra parameter allows for customization of parameter names, types, and doc comments
109    /// along with return types and doc comments.
110    ///
111    /// # Example
112    ///
113    /// ```
114    /// use mlua_extras::typed::{TypedModuleBuilder, Type};
115    ///
116    /// TypedModuleBuilder::default()
117    ///     // Can use `None` instead of `()` for specifying the doc comment
118    ///     .function_with::<String, String>("getMessage", (), |func| {
119    ///         func.param(0, |param| param.name("name").doc("Name to use when constructing the message"));
120    ///         func.ret(0, |ret| ret.doc("Message constructed using the provided name"))
121    ///     })
122    /// ```
123    pub fn function_with<Params, Returns, F, R>(mut self, name: impl AsRef<str>, doc: impl IntoDocComment, generator: F) -> Self
124    where
125        Params: TypedMultiValue,
126        Returns: TypedMultiValue,
127        F: Fn(&mut FunctionBuilder<Params, Returns>) -> R,
128        R: Any,
129    {
130        let mut builder = FunctionBuilder::default();
131        generator(&mut builder);
132
133        self.functions.insert(name.as_ref().to_string().into(), Func {
134            params: builder.params,
135            returns: builder.returns,
136            doc: doc.into_doc_comment()
137        });
138        self
139    }
140
141    /// Creates a new typed method and adds it to the class's type information.
142    ///
143    /// As with methods in lua, the `self` parameter is implicit and has the same type as the
144    /// parent class.
145    ///
146    /// # Example
147    ///
148    /// ```
149    /// use mlua_extras::typed::{TypedModuleBuilder, Type};
150    ///
151    /// TypedModuleBuilder::default()
152    ///     .method::<String, ()>("greet", "Greet the given name")
153    ///     // Can use `None` instead of `()` for specifying the doc comment
154    ///     .method::<String, ()>("hello", ())
155    /// ```
156    pub fn method<Params, Returns>(mut self, name: impl AsRef<str>, doc: impl IntoDocComment) -> Self
157    where
158        Params: TypedMultiValue,
159        Returns: TypedMultiValue,
160    {
161        self.methods.insert(name.as_ref().to_string().into(), Func::new::<Params, Returns>(doc));
162        self
163    }
164
165    /// Same as [`method`][TypedModuleBuilder::method] but with an extra generator function
166    /// parameter.
167    ///
168    /// This extra parameter allows for customization of parameter names, types, and doc comments
169    /// along with return types and doc comments.
170    ///
171    /// # Example
172    ///
173    /// ```
174    /// use mlua_extras::typed::{TypedModuleBuilder, Type};
175    ///
176    /// TypedModuleBuilder::default()
177    ///     // Can use `None` instead of `()` for specifying the doc comment
178    ///     .method_with::<String, String>("getMessage", (), |func| {
179    ///         func.param(0, |param| param.name("name").doc("Name to use when constructing the message"));
180    ///         func.ret(0, |ret| ret.doc("Message constructed using the provided name"))
181    ///     })
182    /// ```
183    pub fn method_with<Params, Returns, F, R>(mut self, name: impl AsRef<str>, doc: impl IntoDocComment, generator: F) -> Self
184    where
185        Params: TypedMultiValue,
186        Returns: TypedMultiValue,
187        F: Fn(&mut FunctionBuilder<Params, Returns>) -> R,
188        R: Any,
189    {
190        let mut builder = FunctionBuilder::default();
191        generator(&mut builder);
192
193        self.methods.insert(name.as_ref().to_string().into(), Func {
194            params: builder.params,
195            returns: builder.returns,
196            doc: doc.into_doc_comment()
197        });
198        self
199    }
200
201    /// Creates a new typed field and adds it to the class's meta type information
202    ///
203    /// # Example
204    ///
205    /// ```
206    /// use mlua_extras::typed::{TypedModuleBuilder, Type};
207    ///
208    /// static NAME: &str = "mlua_extras";
209    ///
210    /// TypedModuleBuilder::default()
211    ///     .meta_field("data1", Type::string() | Type::nil(), "doc comment goes last")
212    ///     .meta_field("data2", Type::array(Type::string()), ()) // Can also use `None` instead of `()`
213    ///     .meta_field("message", Type::string(), foramt!("A message for {NAME}"))
214    /// ```
215    pub fn meta_field<S: AsRef<str>>(mut self, name: impl AsRef<str>, ty: Type, doc: impl IntoDocComment) -> Self {
216        self.meta_fields.insert(name.as_ref().to_string().into(), Field::new(ty, doc));
217        self
218    }
219
220    /// Creates a new typed function and adds it to the class's meta type information
221    ///
222    /// # Example
223    ///
224    /// ```
225    /// use mlua_extras::typed::{TypedModuleBuilder, Type};
226    ///
227    /// TypedModuleBuilder::default()
228    ///     .meta_function::<String, ()>("greet", "Greet the given name")
229    ///     // Can use `None` instead of `()` for specifying the doc comment
230    ///     .meta_function::<String, ()>("hello", ())
231    /// ```
232    pub fn meta_function<Params, Returns>(mut self, name: impl AsRef<str>, doc: impl IntoDocComment) -> Self
233    where
234        Params: TypedMultiValue,
235        Returns: TypedMultiValue,
236    {
237        self.meta_functions.insert(name.as_ref().to_string().into(), Func::new::<Params, Returns>(doc));
238        self
239    }
240
241    /// Same as [`meta_function`][TypedModuleBuilder::meta_function] but with an extra generator function
242    /// parameter.
243    ///
244    /// This extra parameter allows for customization of parameter names, types, and doc comments
245    /// along with return types and doc comments.
246    ///
247    /// # Example
248    ///
249    /// ```
250    /// use mlua_extras::typed::{TypedModuleBuilder, Type};
251    ///
252    /// TypedModuleBuilder::default()
253    ///     // Can use `None` instead of `()` for specifying the doc comment
254    ///     .meta_function_with::<String, String>("getMessage", (), |func| {
255    ///         func.param(0, |param| param.name("name").doc("Name to use when constructing the message"));
256    ///         func.ret(0, |ret| ret.doc("Message constructed using the provided name"))
257    ///     })
258    /// ```
259    pub fn meta_function_with<Params, Returns, F, R>(mut self, name: impl AsRef<str>, doc: impl IntoDocComment, generator: F) -> Self
260    where
261        F: Fn(&mut FunctionBuilder<Params, Returns>) -> R,
262        R: Any,
263        Params: TypedMultiValue,
264        Returns: TypedMultiValue,
265    {
266        let mut builder = FunctionBuilder::default();
267        generator(&mut builder);
268
269        self.meta_functions.insert(name.as_ref().to_string().into(), Func {
270            params: builder.params,
271            returns: builder.returns,
272            doc: doc.into_doc_comment()
273        });
274        self
275    }
276
277    /// Creates a new typed method and adds it to the class's type information.
278    ///
279    /// As with methods in lua, the `self` parameter is implicit and has the same type as the
280    /// parent class.
281    ///
282    /// # Example
283    ///
284    /// ```
285    /// use mlua_extras::typed::{TypedModuleBuilder, Type};
286    ///
287    /// static NAME: &str = "mlua_extras";
288    ///
289    /// TypedModuleBuilder::default()
290    ///     .method::<String, ()>("greet", "Greet the given name")
291    ///     // Can use `None` instead of `()` for specifying the doc comment
292    ///     .method::<String, ()>("hello", ())
293    /// ```
294    pub fn meta_method<Params, Returns>(mut self, name: impl AsRef<str>, doc: impl IntoDocComment) -> Self
295    where
296        Params: TypedMultiValue,
297        Returns: TypedMultiValue,
298    {
299        self.meta_methods.insert(name.as_ref().to_string().into(), Func::new::<Params, Returns>(doc));
300        self
301    }
302
303    /// Same as [`meta_method`][TypedModuleBuilder::meta_method] but with an extra generator function
304    /// parameter.
305    ///
306    /// This extra parameter allows for customization of parameter names, types, and doc comments
307    /// along with return types and doc comments.
308    ///
309    /// # Example
310    ///
311    /// ```
312    /// use mlua_extras::typed::{TypedModuleBuilder, Type};
313    ///
314    /// TypedModuleBuilder::default()
315    ///     // Can use `None` instead of `()` for specifying the doc comment
316    ///     .meta_method_with::<String, String>("getMessage", (), |func| {
317    ///         func.param(0, |param| param.name("name").doc("Name to use when constructing the message"));
318    ///         func.ret(0, |ret| ret.doc("Message constructed using the provided name"))
319    ///     })
320    /// ```
321    pub fn meta_method_with<Params, Returns, F, R>(mut self, name: impl AsRef<str>, doc: impl IntoDocComment, generator: F) -> Self
322    where
323        F: Fn(&mut FunctionBuilder<Params, Returns>) -> R,
324        R: Any,
325        Params: TypedMultiValue,
326        Returns: TypedMultiValue,
327    {
328        let mut builder = FunctionBuilder::default();
329        generator(&mut builder);
330
331        self.meta_methods.insert(name.as_ref().to_string().into(), Func {
332            params: builder.params,
333            returns: builder.returns,
334            doc: doc.into_doc_comment()
335        });
336        self
337    }
338}
339
340/// Typed variant of [`ModuleFields`]
341pub trait TypedModuleFields<'lua> {
342    /// Queue a doc comment to be used with the nest `add` call
343    fn document<V: AsRef<str>>(&mut self, doc: V) -> &mut Self;
344
345    /// Typed variant of [`add_field`][ModuleFields::add_field] only collecting the type information
346    fn add_field<K, V>(&mut self, name: K, value: V) -> mlua::Result<()>
347    where
348        K: Into<Index>,
349        V: IntoLua<'lua> + Typed;
350
351    /// Typed variant of [`add_meta_field`][ModuleFields::add_meta_field] only collecting the type information
352    fn add_meta_field<K, V>(&mut self, name: K, value: V) -> mlua::Result<()>
353    where
354        K: Into<Index>,
355        V: IntoLua<'lua> + Typed;
356
357    /// Typed variant of [`add_module`][ModuleFields::add_module] only collecting the type information
358    fn add_module<V>(&mut self, name: impl Into<Index>) -> mlua::Result<()>
359    where
360        V: TypedModule;
361}
362
363/// Typed variant of [`ModuleMethods`]
364pub trait TypedModuleMethods<'lua> {
365    /// Queue a doc comment to be used with the nest `add` call
366    fn document<V: AsRef<str>>(&mut self, doc: V) -> &mut Self;
367
368    /// Typed variant of [`add_function`][ModuleMethods::add_function] only collecting the type information
369    fn add_function<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
370    where
371        K: Into<Index>,
372        F: Fn(&'lua mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
373        A: FromLuaMulti<'lua> + TypedMultiValue,
374        R: IntoLuaMulti<'lua> + TypedMultiValue;
375
376    /// Typed variant of [`add_function`][ModuleMethods::add_function] only collecting the type information
377    ///
378    /// Pass an additional callback that allows for param names, param doc comments, and return doc
379    /// comments to be specified.
380    fn add_function_with<K, F, A, R, G>(
381        &mut self,
382        name: K,
383        function: F,
384        generator: G,
385    ) -> mlua::Result<()>
386    where
387        K: Into<Index>,
388        F: Fn(&'lua mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
389        A: FromLuaMulti<'lua> + TypedMultiValue,
390        R: IntoLuaMulti<'lua> + TypedMultiValue,
391        G: Fn(&mut FunctionBuilder<A, R>);
392
393    /// Typed variant of [`add_meta_function`][ModuleMethods::add_meta_function] only collecting the type information
394    fn add_meta_function<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
395    where
396        K: Into<Index>,
397        F: Fn(&'lua mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
398        A: FromLuaMulti<'lua> + TypedMultiValue,
399        R: IntoLuaMulti<'lua> + TypedMultiValue;
400
401    /// Typed variant of [`add_meta_function`][ModuleMethods::add_meta_function] only collecting the type information
402    ///
403    /// Pass an additional callback that allows for param names, param doc comments, and return doc
404    /// comments to be specified.
405    fn add_meta_function_with<K, F, A, R, G>(
406        &mut self,
407        name: K,
408        function: F,
409        generator: G,
410    ) -> mlua::Result<()>
411    where
412        K: Into<Index>,
413        F: Fn(&'lua mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
414        A: FromLuaMulti<'lua> + TypedMultiValue,
415        R: IntoLuaMulti<'lua> + TypedMultiValue,
416        G: Fn(&mut FunctionBuilder<A, R>);
417
418    /// Typed variant of [`add_method`][ModuleMethods::add_method] only collecting the type information
419    fn add_method<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
420    where
421        K: Into<Index>,
422        F: Fn(&'lua mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
423        A: FromLuaMulti<'lua> + TypedMultiValue,
424        R: IntoLuaMulti<'lua> + TypedMultiValue;
425
426    /// Typed variant of [`add_method`][ModuleMethods::add_method] only collecting the type information
427    ///
428    /// Pass an additional callback that allows for param names, param doc comments, and return doc
429    /// comments to be specified.
430    fn add_method_with<K, F, A, R, G>(
431        &mut self,
432        name: K,
433        function: F,
434        generator: G,
435    ) -> mlua::Result<()>
436    where
437        K: Into<Index>,
438        F: Fn(&'lua mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
439        A: FromLuaMulti<'lua> + TypedMultiValue,
440        R: IntoLuaMulti<'lua> + TypedMultiValue,
441        G: Fn(&mut FunctionBuilder<A, R>);
442
443    /// Typed variant of [`add_meta_method`][ModuleMethods::add_meta_method] only collecting the type information
444    fn add_meta_method<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
445    where
446        K: Into<Index>,
447        F: Fn(&'lua mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
448        A: FromLuaMulti<'lua> + TypedMultiValue,
449        R: IntoLuaMulti<'lua> + TypedMultiValue;
450
451    /// Typed variant of [`add_meta_method`][ModuleMethods::add_meta_method] only collecting the type information
452    ///
453    /// Pass an additional callback that allows for param names, param doc comments, and return doc
454    /// comments to be specified.
455    fn add_meta_method_with<K, F, A, R, G>(
456        &mut self,
457        name: K,
458        function: F,
459        generator: G,
460    ) -> mlua::Result<()>
461    where
462        K: Into<Index>,
463        F: Fn(&'lua mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
464        A: FromLuaMulti<'lua> + TypedMultiValue,
465        R: IntoLuaMulti<'lua> + TypedMultiValue,
466        G: Fn(&mut FunctionBuilder<A, R>);
467}
468
469pub struct WrappedModule<'module, M>(pub &'module mut M);
470impl<'module, 'lua, M: ModuleFields<'lua>> TypedModuleFields<'lua> for WrappedModule<'module, M> {
471    fn document<V: AsRef<str>>(&mut self, _doc: V) -> &mut Self {
472        self
473    }
474
475    fn add_field<K, V>(&mut self, name: K, value: V) -> mlua::Result<()>
476    where
477        K: Into<Index>,
478        V: IntoLua<'lua> + Typed,
479    {
480        self.0.add_field(name.into(), value)
481    }
482
483    fn add_meta_field<K, V>(&mut self, name: K, value: V) -> mlua::Result<()>
484    where
485        K: Into<Index>,
486        V: IntoLua<'lua> + Typed,
487    {
488        self.0.add_meta_field(name.into(), value)
489    }
490
491    fn add_module<V>(&mut self, name: impl Into<Index>) -> mlua::Result<()>
492    where
493        V: TypedModule,
494    {
495        self.0.add_module::<Index, V>(name.into())
496    }
497}
498
499impl<'module, 'lua, M: ModuleMethods<'lua>> TypedModuleMethods<'lua> for WrappedModule<'module, M> {
500    fn document<V: AsRef<str>>(&mut self, _doc: V) -> &mut Self {
501        self
502    }
503
504    fn add_function<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
505    where
506        K: Into<Index>,
507        F: Fn(&'lua mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
508        A: FromLuaMulti<'lua> + TypedMultiValue,
509        R: IntoLuaMulti<'lua> + TypedMultiValue,
510    {
511        self.0
512            .add_function::<Index, F, A, R>(name.into(), function)
513    }
514
515    fn add_function_with<K, F, A, R, G>(
516        &mut self,
517        name: K,
518        function: F,
519        _generator: G,
520    ) -> mlua::Result<()>
521    where
522        K: Into<Index>,
523        F: Fn(&'lua mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
524        A: FromLuaMulti<'lua> + TypedMultiValue,
525        R: IntoLuaMulti<'lua> + TypedMultiValue,
526        G: Fn(&mut FunctionBuilder<A, R>),
527    {
528        self.0
529            .add_function::<Index, F, A, R>(name.into(), function)
530    }
531
532    fn add_meta_function<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
533    where
534        K: Into<Index>,
535        F: Fn(&'lua mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
536        A: FromLuaMulti<'lua> + TypedMultiValue,
537        R: IntoLuaMulti<'lua> + TypedMultiValue,
538    {
539        self.0.add_meta_function::<Index, F, A, R>(name.into(), function)
540    }
541
542    fn add_meta_function_with<K, F, A, R, G>(
543        &mut self,
544        name: K,
545        function: F,
546        _generator: G,
547    ) -> mlua::Result<()>
548    where
549        K: Into<Index>,
550        F: Fn(&'lua mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
551        A: FromLuaMulti<'lua> + TypedMultiValue,
552        R: IntoLuaMulti<'lua> + TypedMultiValue,
553        G: Fn(&mut FunctionBuilder<A, R>),
554    {
555        self.0.add_meta_function::<Index, F, A, R>(name.into(), function)
556    }
557
558    fn add_method<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
559    where
560        K: Into<Index>,
561        F: Fn(&'lua mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
562        A: FromLuaMulti<'lua> + TypedMultiValue,
563        R: IntoLuaMulti<'lua> + TypedMultiValue,
564    {
565        self.0
566            .add_method::<Index, F, A, R>(name.into(), function)
567    }
568
569    fn add_method_with<K, F, A, R, G>(
570        &mut self,
571        name: K,
572        function: F,
573        _generator: G,
574    ) -> mlua::Result<()>
575    where
576        K: Into<Index>,
577        F: Fn(&'lua mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
578        A: FromLuaMulti<'lua> + TypedMultiValue,
579        R: IntoLuaMulti<'lua> + TypedMultiValue,
580        G: Fn(&mut FunctionBuilder<A, R>),
581    {
582        self.0
583            .add_method::<Index, F, A, R>(name.into(), function)
584    }
585
586    fn add_meta_method<K, F, A, R>(&mut self, name: K, function: F) -> mlua::Result<()>
587    where
588        K: Into<Index>,
589        F: Fn(&'lua mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
590        A: FromLuaMulti<'lua> + TypedMultiValue,
591        R: IntoLuaMulti<'lua> + TypedMultiValue,
592    {
593        self.0.add_meta_method::<Index, F, A, R>(name.into(), function)
594    }
595
596    fn add_meta_method_with<K, F, A, R, G>(
597        &mut self,
598        name: K,
599        function: F,
600        _generator: G,
601    ) -> mlua::Result<()>
602    where
603        K: Into<Index>,
604        F: Fn(&'lua mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
605        A: FromLuaMulti<'lua> + TypedMultiValue,
606        R: IntoLuaMulti<'lua> + TypedMultiValue,
607        R: IntoLuaMulti<'lua> + TypedMultiValue,
608    {
609        self.0
610            .add_meta_method::<Index, F, A, R>(name.into(), function)
611    }
612}
613
614impl<'lua> TypedModuleFields<'lua> for TypedModuleBuilder {
615    fn document<V: AsRef<str>>(&mut self, doc: V) -> &mut Self {
616        self.queued_doc = Some(doc.as_ref().into());
617        self
618    }
619
620    fn add_module<V>(&mut self, name: impl Into<Index>) -> mlua::Result<()>
621    where
622        V: TypedModule,
623    {
624        if self.parents.contains(&type_name::<V>()) {
625            return Err(mlua::Error::runtime(format!(
626                "infinite nested modules using: '{}'",
627                type_name::<V>()
628            )));
629        }
630
631        let mut nested = TypedModuleBuilder {
632            parents: self
633                .parents
634                .iter()
635                .map(|v| *v)
636                .chain([type_name::<V>()])
637                .collect(),
638            ..Default::default()
639        };
640
641        if let Some(doc) = V::documentation() {
642            nested.doc = Some(doc.into());
643        }
644
645        V::add_fields(&mut nested)?;
646        V::add_methods(&mut nested)?;
647
648        self.nested_modules.insert(name.into(), nested);
649        Ok(())
650    }
651
652    fn add_field<K, V>(&mut self, name: K, _value: V) -> mlua::Result<()>
653    where
654        K: Into<Index>,
655        V: IntoLua<'lua> + Typed,
656    {
657        self.fields.insert(
658            name.into(),
659            Field {
660                ty: V::ty(),
661                doc: self.queued_doc.take().map(|v| v.into()),
662            },
663        );
664        Ok(())
665    }
666
667    fn add_meta_field<K, V>(&mut self, name: K, _value: V) -> mlua::Result<()>
668    where
669        K: Into<Index>,
670        V: IntoLua<'lua> + Typed,
671    {
672        self.meta_fields.insert(
673            name.into(),
674            Field {
675                ty: V::ty(),
676                doc: self.queued_doc.take().map(|v| v.into()),
677            },
678        );
679        Ok(())
680    }
681}
682
683impl<'lua> TypedModuleMethods<'lua> for TypedModuleBuilder {
684    fn document<V: AsRef<str>>(&mut self, doc: V) -> &mut Self {
685        self.queued_doc = Some(doc.as_ref().into());
686        self
687    }
688
689    fn add_function<K, F, A, R>(&mut self, name: K, _function: F) -> mlua::Result<()>
690    where
691        K: Into<Index>,
692        F: Fn(&'lua mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
693        A: FromLuaMulti<'lua> + TypedMultiValue,
694        R: IntoLuaMulti<'lua> + TypedMultiValue,
695    {
696        self.functions.insert(
697            name.into(),
698            Func {
699                params: A::get_types_as_params(),
700                returns: R::get_types_as_returns(),
701                doc: self.queued_doc.take().map(|v| v.into()),
702            },
703        );
704        Ok(())
705    }
706
707    fn add_function_with<K, F, A, R, G>(
708        &mut self,
709        name: K,
710        _function: F,
711        generator: G,
712    ) -> mlua::Result<()>
713    where
714        K: Into<Index>,
715        F: Fn(&'lua mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
716        A: FromLuaMulti<'lua> + TypedMultiValue,
717        R: IntoLuaMulti<'lua> + TypedMultiValue,
718        G: Fn(&mut FunctionBuilder<A, R>),
719    {
720        let mut builder = FunctionBuilder::<A, R>::default();
721        generator(&mut builder);
722
723        self.functions.insert(
724            name.into(),
725            Func {
726                params: builder.params,
727                returns: builder.returns,
728                doc: self.queued_doc.take().map(|v| v.into()),
729            },
730        );
731        Ok(())
732    }
733
734    fn add_meta_function<K, F, A, R>(&mut self, name: K, _function: F) -> mlua::Result<()>
735    where
736        K: Into<Index>,
737        F: Fn(&'lua mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
738        A: FromLuaMulti<'lua> + TypedMultiValue,
739        R: IntoLuaMulti<'lua> + TypedMultiValue,
740    {
741        self.meta_functions.insert(
742            name.into(),
743            Func {
744                params: A::get_types_as_params(),
745                returns: R::get_types_as_returns(),
746                doc: self.queued_doc.take().map(|v| v.into()),
747            },
748        );
749        Ok(())
750    }
751
752    fn add_meta_function_with<K, F, A, R, G>(
753        &mut self,
754        name: K,
755        _function: F,
756        generator: G,
757    ) -> mlua::Result<()>
758    where
759        K: Into<Index>,
760        F: Fn(&'lua mlua::Lua, A) -> mlua::Result<R> + MaybeSend + 'static,
761        A: FromLuaMulti<'lua> + TypedMultiValue,
762        R: IntoLuaMulti<'lua> + TypedMultiValue,
763        G: Fn(&mut FunctionBuilder<A, R>),
764    {
765        let mut builder = FunctionBuilder::<A, R>::default();
766        generator(&mut builder);
767
768        self.meta_functions.insert(
769            name.into(),
770            Func {
771                params: builder.params,
772                returns: builder.returns,
773                doc: self.queued_doc.take().map(|v| v.into()),
774            },
775        );
776        Ok(())
777    }
778
779    fn add_method<K, F, A, R>(&mut self, name: K, _function: F) -> mlua::Result<()>
780    where
781        K: Into<Index>,
782        F: Fn(&'lua mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
783        A: FromLuaMulti<'lua> + TypedMultiValue,
784        R: IntoLuaMulti<'lua> + TypedMultiValue,
785    {
786        self.methods.insert(
787            name.into(),
788            Func {
789                params: A::get_types_as_params(),
790                returns: R::get_types_as_returns(),
791                doc: self.queued_doc.take().map(|v| v.into()),
792            },
793        );
794        Ok(())
795    }
796
797    fn add_method_with<K, F, A, R, G>(
798        &mut self,
799        name: K,
800        _function: F,
801        generator: G,
802    ) -> mlua::Result<()>
803    where
804        K: Into<Index>,
805        F: Fn(&'lua mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
806        A: FromLuaMulti<'lua> + TypedMultiValue,
807        R: IntoLuaMulti<'lua> + TypedMultiValue,
808        G: Fn(&mut FunctionBuilder<A, R>),
809    {
810        let mut builder = FunctionBuilder::<A, R>::default();
811        generator(&mut builder);
812
813        self.methods.insert(
814            name.into(),
815            Func {
816                params: builder.params,
817                returns: builder.returns,
818                doc: self.queued_doc.take().map(|v| v.into()),
819            },
820        );
821        Ok(())
822    }
823
824    fn add_meta_method<K, F, A, R>(&mut self, name: K, _function: F) -> mlua::Result<()>
825    where
826        K: Into<Index>,
827        F: Fn(&'lua mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
828        A: FromLuaMulti<'lua> + TypedMultiValue,
829        R: IntoLuaMulti<'lua> + TypedMultiValue,
830    {
831        self.meta_methods.insert(
832            name.into(),
833            Func {
834                params: A::get_types_as_params(),
835                returns: R::get_types_as_returns(),
836                doc: self.queued_doc.take().map(|v| v.into()),
837            },
838        );
839        Ok(())
840    }
841
842    fn add_meta_method_with<K, F, A, R, G>(
843        &mut self,
844        name: K,
845        _function: F,
846        generator: G,
847    ) -> mlua::Result<()>
848    where
849        K: Into<Index>,
850        F: Fn(&'lua mlua::Lua, mlua::Table<'_>, A) -> mlua::Result<R> + MaybeSend + 'static,
851        A: FromLuaMulti<'lua> + TypedMultiValue,
852        R: IntoLuaMulti<'lua> + TypedMultiValue,
853        G: Fn(&mut FunctionBuilder<A, R>),
854    {
855        let mut builder = FunctionBuilder::<A, R>::default();
856        generator(&mut builder);
857
858        self.meta_methods.insert(
859            name.into(),
860            Func {
861                params: builder.params,
862                returns: builder.returns,
863                doc: self.queued_doc.take().map(|v| v.into()),
864            },
865        );
866        Ok(())
867    }
868}
869
870/// Sepecify a lua module (table) with fields and methods.
871///
872/// Only collects documentation and type information
873pub trait TypedModule: Sized {
874    /// Add module level documentation
875    #[inline]
876    fn documentation() -> Option<String> { None }
877
878    /// Add fields to the module
879    #[allow(unused_variables)]
880    fn add_fields<'lua, F: TypedModuleFields<'lua>>(fields: &mut F) -> mlua::Result<()> {
881        Ok(())
882    }
883
884    /// Add methods/functions to the module
885    #[allow(unused_variables)]
886    fn add_methods<'lua, M: TypedModuleMethods<'lua>>(methods: &mut M) -> mlua::Result<()> {
887        Ok(())
888    }
889}
890
891impl<T: TypedModule> Module for T {
892    fn add_fields<'lua, F: ModuleFields<'lua>>(fields: &mut F) -> mlua::Result<()> {
893        let mut wrapped = WrappedModule(fields);
894        T::add_fields(&mut wrapped)
895    }
896
897    fn add_methods<'lua, M: ModuleMethods<'lua>>(methods: &mut M) -> mlua::Result<()> {
898        let mut wrapped = WrappedModule(methods);
899        T::add_methods(&mut wrapped)
900    }
901}