mlua_extras/typed/
mod.rs

1mod function;
2pub mod generator;
3
4mod class;
5mod module;
6
7pub use class::{
8    TypedClassBuilder, TypedDataFields, TypedDataMethods, TypedUserData, WrappedBuilder,
9};
10pub use module::{TypedModule, TypedModuleBuilder, TypedModuleFields, TypedModuleMethods};
11
12use std::{
13    borrow::Cow,
14    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
15};
16
17use function::Return;
18pub use function::{Param, TypedFunction};
19
20use mlua::Variadic;
21
22/// Add a lua [`Type`] representation to a rust type
23pub trait Typed {
24    /// Get the type representation
25    fn ty() -> Type;
26
27    /// Get the type as a function parameter
28    fn as_param() -> Param {
29        Param {
30            doc: None,
31            name: None,
32            ty: Self::ty(),
33        }
34    }
35}
36
37macro_rules! impl_static_typed {
38    {
39        $(
40            $($target: ty)|*
41            => $name: literal),*
42            $(,)?
43    } => {
44        $(
45            $(
46                impl Typed for $target {
47                    fn ty() -> Type {
48                        Type::single($name)
49                    }
50                }
51            )*
52        )*
53    };
54}
55
56macro_rules! impl_static_typed_generic {
57    {
58        $(
59            $(for<$($lt: lifetime),+> $target: ty)|*
60            => $name: literal),*
61            $(,)?
62    } => {
63        $(
64            $(
65                impl<$($lt,)+> Typed for $target {
66                    fn ty() -> Type {
67                        Type::single($name)
68                    }
69                }
70            )*
71        )*
72    };
73}
74
75impl_static_typed! {
76    mlua::LightUserData => "lightuserdata",
77    mlua::Error => "error",
78    String | &str => "string",
79    u8 | u16 | u32 | u64 | usize | u128 | i8 | i16 | i32 | i64 | isize | i128 => "integer",
80    f32 | f64 => "number",
81    bool => "boolean",
82}
83
84impl_static_typed_generic! {
85    for<'a> Cow<'a, str> => "string",
86    for<'lua> mlua::Function<'lua> => "fun()",
87    for<'lua> mlua::AnyUserData<'lua> => "userdata",
88    for<'lua> mlua::String<'lua> => "string",
89    for<'lua> mlua::Thread<'lua> => "thread",
90}
91
92impl<T: Typed> Typed for Variadic<T> {
93    /// ...type
94    fn ty() -> Type {
95        Type::Variadic(T::ty().into())
96    }
97
98    /// @param ... type
99    fn as_param() -> Param {
100        Param {
101            doc: None,
102            name: Some("...".into()),
103            ty: T::ty(),
104        }
105    }
106}
107
108/// {type} | nil
109impl<T: Typed> Typed for Option<T> {
110    fn ty() -> Type {
111        Type::Union(vec![T::ty(), Type::Single("nil".into())])
112    }
113}
114
115impl From<&'static str> for Type {
116    fn from(value: &'static str) -> Self {
117        Type::Single(value.into())
118    }
119}
120
121impl From<Cow<'static, str>> for Type {
122    fn from(value: Cow<'static, str>) -> Self {
123        Type::Single(value.clone())
124    }
125}
126
127impl From<String> for Type {
128    fn from(value: String) -> Self {
129        Type::Single(value.into())
130    }
131}
132
133// Array type
134
135impl<I: Typed, const N: usize> Typed for [I; N] {
136    fn ty() -> Type {
137        Type::Array(I::ty().into())
138    }
139}
140impl<I: Typed> Typed for Vec<I> {
141    fn ty() -> Type {
142        Type::Array(I::ty().into())
143    }
144}
145impl<I: Typed> Typed for &[I] {
146    fn ty() -> Type {
147        Type::Array(I::ty().into())
148    }
149}
150impl<I: Typed> Typed for HashSet<I> {
151    fn ty() -> Type {
152        Type::Array(I::ty().into())
153    }
154}
155impl<I: Typed> Typed for BTreeSet<I> {
156    fn ty() -> Type {
157        Type::Array(I::ty().into())
158    }
159}
160
161// Map type
162
163impl<K, V> Typed for BTreeMap<K, V>
164where
165    K: Typed,
166    V: Typed,
167{
168    fn ty() -> Type {
169        Type::Map(K::ty().into(), V::ty().into())
170    }
171}
172impl<K, V> Typed for HashMap<K, V>
173where
174    K: Typed,
175    V: Typed,
176{
177    fn ty() -> Type {
178        Type::Map(K::ty().into(), V::ty().into())
179    }
180}
181
182/// Representation of a lua type for a rust type
183#[derive(Debug, Clone, PartialEq, strum::AsRefStr, PartialOrd, Eq, Ord)]
184pub enum Type {
185    /// string
186    /// nil
187    /// boolean
188    /// "literal"
189    /// 3
190    /// ... etc
191    Single(Cow<'static, str>),
192    Value(Box<Type>),
193    /// --- @alias {name} <type>
194    Alias(Box<Type>),
195    /// Same as alias but with a set name predefined
196    /// --- @alias {name} <type>
197    Enum(Cow<'static, str>, Vec<Type>),
198    /// --- @class {name}
199    /// --- @field ...
200    Class(Box<TypedClassBuilder>),
201    /// ```lua
202    /// module = {
203    ///     data = nil,
204    ///     method = function(self) end,
205    /// }
206    /// ````
207    ///
208    /// or flattened
209    ///
210    /// ```lua
211    /// module = {}
212    /// function module:method() end
213    /// module.data = nil
214    /// ```
215    Module(Box<TypedModuleBuilder>),
216    /// { [1]: <type>, [2]: <type>, ...etc }
217    Tuple(Vec<Type>),
218    Struct(BTreeMap<&'static str, Type>),
219    Variadic(Box<Type>),
220    Union(Vec<Type>),
221    Array(Box<Type>),
222    Map(Box<Type>, Box<Type>),
223    Function {
224        params: Vec<Param>,
225        returns: Vec<Return>,
226    },
227}
228
229/// Allows to union types
230///
231/// # Example
232///
233/// ```
234/// use mlua_extras::typed::Type;
235///
236/// Type::single("string") | Type::single("nil")
237/// ```
238impl std::ops::BitOr for Type {
239    type Output = Self;
240
241    fn bitor(self, rhs: Self) -> Self::Output {
242        match (self, rhs) {
243            (Self::Union(mut types), Self::Union(other_types)) => {
244                for ty in other_types {
245                    if !types.contains(&ty) {
246                        types.push(ty);
247                    }
248                }
249                Self::Union(types)
250            }
251            (Self::Union(mut types), other) => {
252                if !types.contains(&other) {
253                    types.push(other)
254                }
255                Self::Union(types)
256            }
257            (current, other) => {
258                if current == other {
259                    current
260                } else {
261                    Self::Union(Vec::from([current, other]))
262                }
263            }
264        }
265    }
266}
267
268impl Type {
269    /// Create a lua type literal for a string. i.e. `"string"`
270    pub fn literal_string<T: std::fmt::Display>(value: T) -> Self {
271        Self::Single(format!("\"{value}\"").into())
272    }
273
274    /// Create a lua type literal from a rust value. i.e. `3`, `true`, etc...
275    pub fn literal<T: std::fmt::Display>(value: T) -> Self {
276        Self::Single(value.to_string().into())
277    }
278
279    /// Create a type that has a single value. i.e. `string`, `number`, etc...
280    pub fn single(value: impl Into<Cow<'static, str>>) -> Self {
281        Self::Single(value.into())
282    }
283
284    /// Create an enum type. This is equal to an [`alias`][crate::typed::Type::Alias]
285    pub fn r#enum(
286        name: impl Into<Cow<'static, str>>,
287        types: impl IntoIterator<Item = Type>,
288    ) -> Self {
289        Self::Enum(name.into(), types.into_iter().collect())
290    }
291
292    /// Create a type that is an alias. i.e. `--- @alias {name} string`
293    pub fn alias(ty: Type) -> Self {
294        Self::Alias(Box::new(ty))
295    }
296
297    /// Create a type that is variadic. i.e. `...type`
298    pub fn variadic(ty: Type) -> Self {
299        Self::Variadic(Box::new(ty))
300    }
301
302    /// Create a type that is an array. i.e. `{ [integer]: type }`
303    pub fn array(ty: Type) -> Self {
304        Self::Array(Box::new(ty))
305    }
306
307    /// Create a type that is a union. i.e. `string | integer | nil`
308    pub fn union(types: impl IntoIterator<Item = Type>) -> Self {
309        Self::Union(types.into_iter().collect())
310    }
311
312    /// create a type that is a tuple. i.e. `{ [1]: type, [2]: type }`
313    pub fn tuple(types: impl IntoIterator<Item = Type>) -> Self {
314        Self::Tuple(types.into_iter().collect())
315    }
316
317    /// create a type that is a class. i.e. `--- @class {name}`
318    pub fn class(class: TypedClassBuilder) -> Self {
319        Self::Class(Box::new(class))
320    }
321
322    /// create a type that is a global module
323    pub fn module(module: TypedModuleBuilder) -> Self {
324        Self::Module(Box::new(module))
325    }
326
327    /// create a type that is a function. i.e. `fun(self): number`
328    pub fn function<Params: TypedMultiValue, Response: TypedMultiValue>() -> Self {
329        Self::Function {
330            params: Params::get_types_as_params(),
331            returns: Response::get_types()
332                .into_iter()
333                .map(|ty| Return { doc: None, ty })
334                .collect(),
335        }
336    }
337}
338
339/// Helper to create a union type
340///
341/// :NOTE: This is a work in progress macro
342///
343/// # Example
344///
345/// ```
346/// use mlua_extras::{union, typed::Type};
347/// union!("string", "number", "nil", Type::array(Type::single("string")))
348/// ```
349#[macro_export]
350macro_rules! union {
351    ($($typ: expr),*) => {
352        $crate::typed::Type::Union(Vec::from([$(Type::from($typ),)*]))
353    };
354}
355
356/// Typed information for a lua [`MultiValue`][mlua::MultiValue]
357pub trait TypedMultiValue {
358    /// Gets the types contained in this collection.
359    /// Order *IS* important.
360    fn get_types() -> Vec<Type> {
361        Self::get_types_as_params()
362            .into_iter()
363            .map(|v| v.ty)
364            .collect::<Vec<_>>()
365    }
366
367    fn get_types_as_returns() -> Vec<Return> {
368        Self::get_types_as_params()
369            .into_iter()
370            .map(|v| Return {
371                doc: None,
372                ty: v.ty,
373            })
374            .collect::<Vec<_>>()
375    }
376
377    /// Gets the type representations as used for function parameters
378    fn get_types_as_params() -> Vec<Param>;
379}
380
381macro_rules! impl_typed_multi_value {
382    () => (
383        impl TypedMultiValue for () {
384            #[allow(unused_mut)]
385            #[allow(non_snake_case)]
386            fn get_types_as_params() -> Vec<Param> {
387                Vec::new()
388            }
389        }
390    );
391    ($($name:ident) +) => (
392        impl<$($name,)* > TypedMultiValue for ($($name,)*)
393            where $($name: Typed,)*
394        {
395            #[allow(unused_mut)]
396            #[allow(non_snake_case)]
397            fn get_types_as_params() -> Vec<Param> {
398                Vec::from([
399                    $($name::as_param(),)*
400                ])
401            }
402        }
403    );
404}
405
406impl<A> TypedMultiValue for A
407where
408    A: Typed,
409{
410    fn get_types_as_params() -> Vec<Param> {
411        Vec::from([A::as_param()])
412    }
413}
414
415impl_typed_multi_value!(A B C D E F G H I J K L M N O P);
416impl_typed_multi_value!(A B C D E F G H I J K L M N O);
417impl_typed_multi_value!(A B C D E F G H I J K L M N);
418impl_typed_multi_value!(A B C D E F G H I J K L M);
419impl_typed_multi_value!(A B C D E F G H I J K L);
420impl_typed_multi_value!(A B C D E F G H I J K);
421impl_typed_multi_value!(A B C D E F G H I J);
422impl_typed_multi_value!(A B C D E F G H I);
423impl_typed_multi_value!(A B C D E F G H);
424impl_typed_multi_value!(A B C D E F G);
425impl_typed_multi_value!(A B C D E F);
426impl_typed_multi_value!(A B C D E);
427impl_typed_multi_value!(A B C D);
428impl_typed_multi_value!(A B C);
429impl_typed_multi_value!(A B);
430impl_typed_multi_value!(A);
431impl_typed_multi_value!();
432
433/// Type information for a lua `class` field
434#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
435pub struct Field {
436    pub ty: Type,
437    pub doc: Option<Cow<'static, str>>,
438}
439
440/// Type information for a lua `class` function
441#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
442pub struct Func {
443    pub params: Vec<Param>,
444    pub returns: Vec<Return>,
445    pub doc: Option<Cow<'static, str>>,
446}