1mod function;
2pub mod generator;
3
4mod class;
5mod module;
6
7pub use class::{
8    TypedClassBuilder, TypedDataFields, TypedDataMethods, TypedDataDocumentation, TypedUserData, WrappedBuilder,
9};
10pub use module::{TypedModule, TypedModuleBuilder, TypedModuleFields, TypedModuleMethods};
11
12use std::{
13    borrow::Cow, cell::{Cell, RefCell}, collections::{BTreeMap, BTreeSet, HashMap, HashSet}, marker::PhantomData, rc::Rc, sync::{Arc, Mutex}
14};
15
16use function::Return;
17pub use function::{Param, TypedFunction};
18
19use mlua::{IntoLua, MetaMethod, Value, Variadic};
20
21pub trait Typed {
23    fn ty() -> Type;
25
26    fn as_param() -> Param {
28        Param {
29            doc: None,
30            name: None,
31            ty: Self::ty(),
32        }
33    }
34}
35
36macro_rules! impl_static_typed {
37    {
38        $(
39            $($target: ty)|*
40            => $name: literal),*
41            $(,)?
42    } => {
43        $(
44            $(
45                impl Typed for $target {
46                    fn ty() -> Type {
47                        Type::named($name)
48                    }
49                }
50            )*
51        )*
52    };
53}
54
55macro_rules! impl_static_typed_generic {
56    {
57        $(
58            $(for<$($lt: lifetime),+> $target: ty)|*
59            => $name: literal),*
60            $(,)?
61    } => {
62        $(
63            $(
64                impl<$($lt,)+> Typed for $target {
65                    fn ty() -> Type {
66                        Type::named($name)
67                    }
68                }
69            )*
70        )*
71    };
72}
73
74impl_static_typed! {
75    mlua::LightUserData => "lightuserdata",
76    mlua::Error => "error",
77    String | &str => "string",
78    u8 | u16 | u32 | u64 | usize | u128 | i8 | i16 | i32 | i64 | isize | i128 => "integer",
79    f32 | f64 => "number",
80    bool => "boolean",
81}
82
83impl_static_typed_generic! {
84    for<'a> Cow<'a, str> => "string",
85    for<'lua> mlua::Function<'lua> => "fun()",
86    for<'lua> mlua::Table<'lua> => "table",
87    for<'lua> mlua::AnyUserData<'lua> => "userdata",
88    for<'lua> mlua::String<'lua> => "string",
89    for<'lua> mlua::Thread<'lua> => "thread",
90}
91
92impl<'lua> Typed for mlua::Value<'lua> {
93    fn ty() -> Type {
94        Type::Single("any".into())
95    }
96}
97
98impl<T: Typed> Typed for Variadic<T> {
99    fn ty() -> Type {
101        Type::any()
102    }
103}
104
105impl<T: Typed> Typed for Option<T> {
107    fn ty() -> Type {
108        Type::Union(vec![T::ty(), Type::Single("nil".into())])
109    }
110}
111
112impl<T: IntoLuaTypeLiteral> From<T> for Type {
113    fn from(value: T) -> Self {
114        Type::Single(value.into_lua_type_literal().into())
115    }
116}
117
118impl<T: Typed> Typed for Arc<T> {
119    fn ty() -> Type {
120        T::ty()
121    }
122}
123
124impl<T: Typed> Typed for Rc<T> {
125    fn ty() -> Type {
126        T::ty()
127    }
128}
129
130impl<T: Typed> Typed for Cell<T> {
131    fn ty() -> Type {
132        T::ty()
133    }
134}
135
136impl<T: Typed> Typed for RefCell<T> {
137    fn ty() -> Type {
138        T::ty()
139    }
140}
141
142impl<T: Typed> Typed for Mutex<T> {
143    fn ty() -> Type {
144        T::ty()
145    }
146}
147
148impl<T: Typed> Typed for PhantomData<T> {
149    fn ty() -> Type {
150        T::ty()
151    }
152}
153
154impl<const N: usize> From<[Type;N]> for Type {
164    fn from(value: [Type;N]) -> Self {
165        Type::Tuple(Vec::from(value))
166    }
167}
168
169impl<I: Typed, const N: usize> Typed for [I; N] {
172    fn ty() -> Type {
173        Type::Array(I::ty().into())
174    }
175}
176
177impl<I: Typed> Typed for Vec<I> {
178    fn ty() -> Type {
179        Type::Array(I::ty().into())
180    }
181}
182
183impl<I: Typed> Typed for &[I] {
184    fn ty() -> Type {
185        Type::Array(I::ty().into())
186    }
187}
188
189impl<I: Typed> Typed for HashSet<I> {
190    fn ty() -> Type {
191        Type::Array(I::ty().into())
192    }
193}
194
195impl<I: Typed> Typed for BTreeSet<I> {
196    fn ty() -> Type {
197        Type::Array(I::ty().into())
198    }
199}
200
201impl<K, V> Typed for BTreeMap<K, V>
204where
205    K: Typed,
206    V: Typed,
207{
208    fn ty() -> Type {
209        Type::Map(K::ty().into(), V::ty().into())
210    }
211}
212
213impl<K, V> Typed for HashMap<K, V>
214where
215    K: Typed,
216    V: Typed,
217{
218    fn ty() -> Type {
219        Type::Map(K::ty().into(), V::ty().into())
220    }
221}
222
223#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
227pub enum Index {
228    Int(usize),
229    Str(Cow<'static, str>),
230}
231
232impl std::fmt::Display for Index {
233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        match self {
235            Self::Int(num) => write!(f, "[{num}]"),
236            Self::Str(val) => if val.chars().any(|v| !v.is_alphanumeric() && v != '_') {
237                write!(f, r#"["{val}"]"#)
238            } else {
239                write!(f, "{val}")
240            }
241        }
242    }
243}
244
245impl<'lua> IntoLua<'lua> for Index {
246    fn into_lua(self, lua: &'lua mlua::Lua) -> mlua::prelude::LuaResult<Value<'lua>> {
247        match self {
248            Self::Int(num) => Ok(mlua::Value::Integer(num as mlua::Integer)),
249            Self::Str(val) => val.into_lua(lua)
250        }
251    }
252}
253
254impl From<MetaMethod> for Index {
255    fn from(value: MetaMethod) -> Self {
256        Self::Str(value.as_ref().to_string().into())
257    }
258}
259
260impl From<Cow<'static, str>> for Index {
261    fn from(value: Cow<'static, str>) -> Self {
262        Self::Str(value)
263    }
264}
265
266impl From<&'static str> for Index {
267    fn from(value: &'static str) -> Self {
268        Self::Str(value.into())
269    }
270}
271
272impl From<String> for Index {
273    fn from(value: String) -> Self {
274        Self::Str(value.into())
275    }
276}
277
278impl From<usize> for Index {
279    fn from(value: usize) -> Self {
280        Self::Int(value)
281    }
282}
283
284#[derive(Debug, Clone, PartialEq, strum::AsRefStr, strum::EnumIs, PartialOrd, Eq, Ord, Hash)]
286pub enum Type {
287    Single(Cow<'static, str>),
299    Value(Box<Type>),
308    Alias(Box<Type>),
316    Tuple(Vec<Type>),
324    Table(BTreeMap<Index, Type>),
332    Union(Vec<Type>),
340    Array(Box<Type>),
347    Map(Box<Type>, Box<Type>),
355    Function {
363        params: Vec<Param>,
364        returns: Vec<Return>,
365    },
366    Enum(Vec<Type>),
380    Class(Box<TypedClassBuilder>),
391    Module(Box<TypedModuleBuilder>),
410}
411
412impl<T: Into<Type>> std::ops::BitOr<T> for Type {
422    type Output = Self;
423
424    fn bitor(self, rhs: T) -> Self::Output {
425        match (self, rhs.into()) {
426            (Self::Union(mut types), Self::Union(other_types)) => {
427                for ty in other_types {
428                    if !types.contains(&ty) {
429                        types.push(ty);
430                    }
431                }
432                Self::Union(types)
433            }
434            (Self::Union(mut types), other) => {
435                if !types.contains(&other) {
436                    types.push(other)
437                }
438                Self::Union(types)
439            }
440            (current, other) => {
441                if current == other {
442                    current
443                } else {
444                    Self::Union(Vec::from([current, other]))
445                }
446            }
447        }
448    }
449}
450
451impl Type {
452    pub fn literal<T: IntoLuaTypeLiteral>(value: T) -> Self {
454        Self::Single(value.into_lua_type_literal().into())
455    }
456
457    pub fn named(value: impl Into<Cow<'static, str>>) -> Self {
475        Self::Single(value.into())
476    }
477
478    pub fn string() -> Self {
480        Self::Single("string".into())
481    }
482
483    pub fn integer() -> Self {
485        Self::Single("integer".into())
486    }
487
488    pub fn number() -> Self {
490        Self::Single("number".into())
491    }
492
493    pub fn boolean() -> Self {
495        Self::Single("boolean".into())
496    }
497
498    pub fn nil() -> Self {
500        Self::Single("nil".into())
501    }
502
503    pub fn any() -> Self {
505        Self::Single("any".into())
506    }
507
508    pub fn lightuserdata() -> Self {
510        Self::Single("lightuserdata".into())
511    }
512
513    pub fn thread() -> Self {
515        Self::Single("thread".into())
516    }
517
518    pub fn r#enum(
520        types: impl IntoIterator<Item = Type>,
521    ) -> Self {
522        Self::Enum(types.into_iter().collect())
523    }
524
525    pub fn alias(ty: Type) -> Self {
527        Self::Alias(Box::new(ty))
528    }
529
530    pub fn array(ty: Type) -> Self {
532        Self::Array(Box::new(ty))
533    }
534
535    pub fn map(key: Type, value: Type) -> Self {
537        Self::Map(Box::new(key), Box::new(value))
538    }
539
540    pub fn union(types: impl IntoIterator<Item = Type>) -> Self {
542        Self::Union(types.into_iter().collect())
543    }
544
545    pub fn tuple(types: impl IntoIterator<Item = Type>) -> Self {
547        Self::Tuple(types.into_iter().collect())
548    }
549
550    pub fn class(class: TypedClassBuilder) -> Self {
552        Self::Class(Box::new(class))
553    }
554
555    pub fn module(module: TypedModuleBuilder) -> Self {
557        Self::Module(Box::new(module))
558    }
559
560    pub fn function<Params: TypedMultiValue, Response: TypedMultiValue>() -> Self {
562        Self::Function {
563            params: Params::get_types_as_params(),
564            returns: Response::get_types()
565                .into_iter()
566                .map(|ty| Return { doc: None, ty })
567                .collect(),
568        }
569    }
570
571    pub fn table(items: impl IntoIterator<Item=(Index, Type)>) -> Self {
575        Self::Table(items.into_iter().collect())
576    }
577}
578
579pub trait IntoLuaTypeLiteral {
580    fn into_lua_type_literal(self) -> String;
582}
583
584impl IntoLuaTypeLiteral for String {
585    fn into_lua_type_literal(self) -> String {
586        format!("\"{self}\"")
587    }
588}
589
590impl IntoLuaTypeLiteral for &String {
591    fn into_lua_type_literal(self) -> String {
592        format!("\"{self}\"")
593    }
594}
595
596impl IntoLuaTypeLiteral for &str {
597    fn into_lua_type_literal(self) -> String {
598        format!("\"{self}\"")
599    }
600}
601
602macro_rules! impl_type_literal {
603    ($($lit: ty),* $(,)?) => {
604        $(
605            impl IntoLuaTypeLiteral for $lit {
606                fn into_lua_type_literal(self) -> String {
607                    self.to_string()
608                }
609            } 
610            impl IntoLuaTypeLiteral for &$lit {
611                fn into_lua_type_literal(self) -> String {
612                    self.to_string()
613                }
614            } 
615        )*
616    };
617}
618
619impl_type_literal!{
620    u8, u16, u32, u64, usize, u128,
621    i8, i16, i32, i64, isize, i128,
622    f32, f64
623}
624impl_type_literal!{bool}
625
626pub trait TypedMultiValue {
628    fn get_types() -> Vec<Type> {
631        Self::get_types_as_params()
632            .into_iter()
633            .map(|v| v.ty)
634            .collect::<Vec<_>>()
635    }
636
637    fn get_types_as_returns() -> Vec<Return> {
638        Self::get_types_as_params()
639            .into_iter()
640            .map(|v| Return {
641                doc: None,
642                ty: v.ty,
643            })
644            .collect::<Vec<_>>()
645    }
646
647    fn get_types_as_params() -> Vec<Param>;
649}
650
651macro_rules! impl_typed_multi_value {
652    () => (
653        impl TypedMultiValue for () {
654            #[allow(unused_mut)]
655            #[allow(non_snake_case)]
656            fn get_types_as_params() -> Vec<Param> {
657                Vec::new()
658            }
659        }
660    );
661    ($($name:ident) +) => (
662        impl<$($name,)* > TypedMultiValue for ($($name,)*)
663            where $($name: Typed,)*
664        {
665            #[allow(unused_mut)]
666            #[allow(non_snake_case)]
667            fn get_types_as_params() -> Vec<Param> {
668                Vec::from([
669                    $($name::as_param(),)*
670                ])
671            }
672        }
673    );
674}
675
676impl<A> TypedMultiValue for A
677where
678    A: Typed,
679{
680    fn get_types_as_params() -> Vec<Param> {
681        Vec::from([A::as_param()])
682    }
683}
684
685impl_typed_multi_value!(A B C D E F G H I J K L M N O P);
686impl_typed_multi_value!(A B C D E F G H I J K L M N O);
687impl_typed_multi_value!(A B C D E F G H I J K L M N);
688impl_typed_multi_value!(A B C D E F G H I J K L M);
689impl_typed_multi_value!(A B C D E F G H I J K L);
690impl_typed_multi_value!(A B C D E F G H I J K);
691impl_typed_multi_value!(A B C D E F G H I J);
692impl_typed_multi_value!(A B C D E F G H I);
693impl_typed_multi_value!(A B C D E F G H);
694impl_typed_multi_value!(A B C D E F G);
695impl_typed_multi_value!(A B C D E F);
696impl_typed_multi_value!(A B C D E);
697impl_typed_multi_value!(A B C D);
698impl_typed_multi_value!(A B C);
699impl_typed_multi_value!(A B);
700impl_typed_multi_value!(A);
701impl_typed_multi_value!();
702
703#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
705pub struct Field {
706    pub ty: Type,
707    pub doc: Option<Cow<'static, str>>,
708}
709
710impl Field {
711    pub fn new(ty: Type, doc: impl IntoDocComment) -> Self {
712        Self {
713            ty,
714            doc: doc.into_doc_comment()
715        }
716    }
717}
718
719#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
721pub struct Func {
722    pub params: Vec<Param>,
723    pub returns: Vec<Return>,
724    pub doc: Option<Cow<'static, str>>,
725}
726
727impl Func {
728    pub fn new<Params, Returns>(doc: impl IntoDocComment) -> Self
729    where
730        Params: TypedMultiValue,
731        Returns: TypedMultiValue,
732    {
733        Self {
734            params: Params::get_types_as_params(),
735            returns: Returns::get_types_as_returns(),
736            doc: doc.into_doc_comment()
737        }
738    }
739}
740
741pub trait IntoDocComment {
743    fn into_doc_comment(self) -> Option<Cow<'static, str>>;
744}
745
746impl IntoDocComment for String {
747    fn into_doc_comment(self) -> Option<Cow<'static, str>> {
748        Some(self.into())
749    }
750}
751
752impl IntoDocComment for &str {
753    fn into_doc_comment(self) -> Option<Cow<'static, str>> {
754        Some(self.to_string().into())
755    }
756}
757
758impl IntoDocComment for () {
759    fn into_doc_comment(self) -> Option<Cow<'static, str>> {
760        None
761    }
762}
763
764impl IntoDocComment for Option<String> {
765    fn into_doc_comment(self) -> Option<Cow<'static, str>> {
766        self.map(|v| v.into())
767    }
768}