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}