mlua_extras/typed/
module.rs

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/// Builder that constructs type and documentation information for a module using the [`TypedModule`] trait
11#[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
60/// Typed variant of [`ModuleFields`]
61pub trait TypedModuleFields<'lua> {
62    /// Queue a doc comment to be used with the nest `add` call
63    fn document<V: AsRef<str>>(&mut self, doc: V) -> &mut Self;
64
65    /// Typed variant of [`add_field`][ModuleFields::add_field] only collecting the type information
66    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    /// Typed variant of [`add_meta_field`][ModuleFields::add_meta_field] only collecting the type information
72    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    /// Typed variant of [`add_module`][ModuleFields::add_module] only collecting the type information
78    fn add_module<V>(&mut self, name: impl AsRef<str>) -> mlua::Result<()>
79    where
80        V: TypedModule;
81}
82
83/// Typed variant of [`ModuleMethods`]
84pub trait TypedModuleMethods<'lua> {
85    /// Queue a doc comment to be used with the nest `add` call
86    fn document<V: AsRef<str>>(&mut self, doc: V) -> &mut Self;
87
88    /// Typed variant of [`add_function`][ModuleMethods::add_function] only collecting the type information
89    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    /// Typed variant of [`add_function`][ModuleMethods::add_function] only collecting the type information
97    ///
98    /// Pass an additional callback that allows for param names, param doc comments, and return doc
99    /// comments to be specified.
100    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    /// Typed variant of [`add_meta_function`][ModuleMethods::add_meta_function] only collecting the type information
114    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    /// Typed variant of [`add_meta_function`][ModuleMethods::add_meta_function] only collecting the type information
122    ///
123    /// Pass an additional callback that allows for param names, param doc comments, and return doc
124    /// comments to be specified.
125    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    /// Typed variant of [`add_method`][ModuleMethods::add_method] only collecting the type information
139    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    /// Typed variant of [`add_method`][ModuleMethods::add_method] only collecting the type information
147    ///
148    /// Pass an additional callback that allows for param names, param doc comments, and return doc
149    /// comments to be specified.
150    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    /// Typed variant of [`add_meta_method`][ModuleMethods::add_meta_method] only collecting the type information
164    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    /// Typed variant of [`add_meta_method`][ModuleMethods::add_meta_method] only collecting the type information
172    ///
173    /// Pass an additional callback that allows for param names, param doc comments, and return doc
174    /// comments to be specified.
175    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
590/// Sepecify a lua module (table) with fields and methods.
591///
592/// Only collects documentation and type information
593pub trait TypedModule: Sized {
594    /// Add module level documentation
595    #[inline]
596    fn documentation() -> Option<String> { None }
597
598    /// Add fields to the module
599    #[allow(unused_variables)]
600    fn add_fields<'lua, F: TypedModuleFields<'lua>>(fields: &mut F) -> mlua::Result<()> {
601        Ok(())
602    }
603
604    /// Add methods/functions to the module
605    #[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}