1mod function;
2pub mod generator;
3
4mod class;
5
6pub use class::{
7 TypedClassBuilder, TypedClass, TypedDataDocumentation, TypedDataFields, TypedDataMethods, TypedUserData,
8 WrappedBuilder,
9};
10
11use std::{
12 borrow::Cow, collections::{BTreeMap, BTreeSet, HashMap, HashSet}, marker::PhantomData
13};
14#[cfg(feature="userdata-wrappers")]
15use std::{sync::{Arc, Mutex}, cell::{Cell, RefCell}, rc::Rc};
16
17pub use function::{Param, Return, TypedFunction};
18
19use mlua::{IntoLua, MetaMethod, Value, Variadic};
20
21#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, strum::EnumIs)]
25pub enum Index {
26 Int(isize),
27 Str(Cow<'static, str>),
28}
29
30impl std::fmt::Display for Index {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 Self::Int(num) => write!(f, "[{num}]"),
34 Self::Str(val) => {
35 if val.chars().any(|v| !v.is_alphanumeric() && v != '_') {
36 write!(f, r#"["{val}"]"#)
37 } else {
38 write!(f, "{val}")
39 }
40 }
41 }
42 }
43}
44
45impl IntoLua for Index {
46 fn into_lua(self, lua: &mlua::Lua) -> mlua::prelude::LuaResult<Value> {
47 match self {
48 Self::Int(num) => Ok(mlua::Value::Integer(num as mlua::Integer)),
49 Self::Str(val) => val.into_lua(lua),
50 }
51 }
52}
53
54impl From<MetaMethod> for Index {
55 fn from(value: MetaMethod) -> Self {
56 Self::Str(value.as_ref().to_string().into())
57 }
58}
59
60impl From<Cow<'static, str>> for Index {
61 fn from(value: Cow<'static, str>) -> Self {
62 Self::Str(value)
63 }
64}
65
66impl From<&'static str> for Index {
67 fn from(value: &'static str) -> Self {
68 Self::Str(value.into())
69 }
70}
71
72impl From<String> for Index {
73 fn from(value: String) -> Self {
74 Self::Str(value.into())
75 }
76}
77
78impl From<isize> for Index {
79 fn from(value: isize) -> Self {
80 Self::Int(value)
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, strum::AsRefStr, strum::EnumIs, PartialOrd, Eq, Ord, Hash)]
86pub enum Type {
87 Single(Cow<'static, str>),
99 Value(Box<Type>),
108 Alias(Box<Type>),
116 Tuple(Vec<Type>),
124 Table(BTreeMap<Index, Type>),
132 Union(Vec<Type>),
140 Array(Box<Type>),
147 Map(Box<Type>, Box<Type>),
155 Function {
163 params: Vec<Param>,
164 returns: Vec<Return>,
165 },
166 Enum(Vec<Type>),
180 Class(Box<TypedClass>),
191}
192
193impl<T: Into<Type>> std::ops::BitOr<T> for Type {
203 type Output = Self;
204
205 fn bitor(self, rhs: T) -> Self::Output {
206 match (self, rhs.into()) {
207 (Self::Union(mut types), Self::Union(other_types)) => {
208 for ty in other_types {
209 if !types.contains(&ty) {
210 types.push(ty);
211 }
212 }
213 Self::Union(types)
214 }
215 (Self::Union(mut types), other) => {
216 if !types.contains(&other) {
217 types.push(other)
218 }
219 Self::Union(types)
220 }
221 (current, other) => {
222 if current == other {
223 current
224 } else {
225 Self::Union(Vec::from([current, other]))
226 }
227 }
228 }
229 }
230}
231
232impl Type {
233 pub fn literal<T: IntoLuaTypeLiteral>(value: T) -> Self {
235 Self::Single(value.into_lua_type_literal().into())
236 }
237
238 pub fn named(value: impl Into<Cow<'static, str>>) -> Self {
256 Self::Single(value.into())
257 }
258
259 pub fn string() -> Self {
261 Self::Single("string".into())
262 }
263
264 pub fn integer() -> Self {
266 Self::Single("integer".into())
267 }
268
269 pub fn number() -> Self {
271 Self::Single("number".into())
272 }
273
274 pub fn boolean() -> Self {
276 Self::Single("boolean".into())
277 }
278
279 pub fn nil() -> Self {
281 Self::Single("nil".into())
282 }
283
284 pub fn any() -> Self {
286 Self::Single("any".into())
287 }
288
289 pub fn lightuserdata() -> Self {
291 Self::Single("lightuserdata".into())
292 }
293
294 pub fn thread() -> Self {
296 Self::Single("thread".into())
297 }
298
299 pub fn r#enum(types: impl IntoIterator<Item = Type>) -> Self {
301 Self::Enum(types.into_iter().collect())
302 }
303
304 pub fn alias(ty: Type) -> Self {
306 Self::Alias(Box::new(ty))
307 }
308
309 pub fn array(ty: Type) -> Self {
311 Self::Array(Box::new(ty))
312 }
313
314 pub fn map(key: Type, value: Type) -> Self {
316 Self::Map(Box::new(key), Box::new(value))
317 }
318
319 pub fn union(types: impl IntoIterator<Item = Type>) -> Self {
321 Self::Union(types.into_iter().collect())
322 }
323
324 pub fn tuple(types: impl IntoIterator<Item = Type>) -> Self {
326 Self::Tuple(types.into_iter().collect())
327 }
328
329 pub fn class(class: TypedClass) -> Self {
331 Self::Class(Box::new(class))
332 }
333
334 pub fn function<Params: TypedMultiValue, Response: TypedMultiValue>(
336 params: Vec<(String, String)>,
337 returns: Vec<String>,
338 ) -> Self {
339 Self::Function {
340 params: Params::get_types_as_params()
341 .into_iter()
342 .enumerate()
343 .map(|(i, mut v)| {
344 if let Some((name, doc)) = params.get(i) {
345 v.name(name.clone()).doc(doc.as_str());
346 }
347 v
348 })
349 .collect(),
350 returns: Response::get_types_as_returns()
351 .into_iter()
352 .enumerate()
353 .map(|(i, mut v)| {
354 if let Some(doc) = returns.get(i) {
355 v.doc(doc.as_str());
356 }
357 v
358 })
359 .collect(),
360 }
361 }
362
363 pub fn table(items: impl IntoIterator<Item = (Index, Type)>) -> Self {
367 Self::Table(items.into_iter().collect())
368 }
369}
370
371pub trait IntoLuaTypeLiteral {
372 fn into_lua_type_literal(self) -> String;
374}
375
376impl IntoLuaTypeLiteral for String {
377 fn into_lua_type_literal(self) -> String {
378 format!("\"{self}\"")
379 }
380}
381
382impl IntoLuaTypeLiteral for &String {
383 fn into_lua_type_literal(self) -> String {
384 format!("\"{self}\"")
385 }
386}
387
388impl IntoLuaTypeLiteral for &str {
389 fn into_lua_type_literal(self) -> String {
390 format!("\"{self}\"")
391 }
392}
393
394macro_rules! impl_type_literal {
395 ($($lit: ty),* $(,)?) => {
396 $(
397 impl IntoLuaTypeLiteral for $lit {
398 fn into_lua_type_literal(self) -> String {
399 self.to_string()
400 }
401 }
402 impl IntoLuaTypeLiteral for &$lit {
403 fn into_lua_type_literal(self) -> String {
404 self.to_string()
405 }
406 }
407 )*
408 };
409}
410
411impl_type_literal! {
412 u8, u16, u32, u64, usize, u128,
413 i8, i16, i32, i64, isize, i128,
414 f32, f64
415}
416impl_type_literal! {bool}
417
418pub trait Typed {
420 fn ty() -> Type;
422
423 #[inline(always)]
424 fn implicit() -> impl IntoIterator<Item = (&'static str, Type)> {
425 []
426 }
427
428 fn as_param() -> Type {
430 Self::ty()
431 }
432
433 fn as_return() -> Type {
435 Self::ty()
436 }
437}
438
439#[macro_export]
440macro_rules! join_types {
441 ($($ty:expr),+) => {
442 $crate::typed::Type::union([
443 $($crate::typed::Type::from($ty),)+
444 ])
445 };
446}
447
448macro_rules! impl_static_typed {
449 {
450 $(
451 $($target: ty)|*
452 => $name: literal),*
453 $(,)?
454 } => {
455 $(
456 $(
457 impl Typed for $target {
458 fn ty() -> Type {
459 Type::named($name)
460 }
461 }
462 )*
463 )*
464 };
465}
466
467macro_rules! impl_static_typed_generic {
468 {
469 $(
470 $(for<$($lt: lifetime),+> $target: ty)|*
471 => $name: literal),*
472 $(,)?
473 } => {
474 $(
475 $(
476 impl<$($lt,)+> Typed for $target {
477 fn ty() -> Type {
478 Type::named($name)
479 }
480 }
481 )*
482 )*
483 };
484}
485
486impl_static_typed! {
487 mlua::LightUserData => "lightuserdata",
488 mlua::Error => "error",
489 String | &str => "string",
490 u8 | u16 | u32 | u64 | usize | u128 | i8 | i16 | i32 | i64 | isize | i128 => "integer",
491 f32 | f64 => "number",
492 bool => "boolean",
493
494 mlua::Function => "fun()",
495 mlua::Table => "table",
496 mlua::AnyUserData => "userdata",
497 mlua::String => "string",
498 mlua::Thread => "thread",
499}
500
501impl_static_typed_generic! {
502 for<'a> Cow<'a, str> => "string",
503}
504
505impl Typed for mlua::Value {
506 fn ty() -> Type {
507 Type::Single("any".into())
508 }
509}
510
511impl<T: Typed> Typed for Variadic<T> {
512 fn ty() -> Type {
514 Type::any()
515 }
516}
517
518impl<T: IntoLuaTypeLiteral> From<T> for Type {
519 fn from(value: T) -> Self {
520 Type::Single(value.into_lua_type_literal().into())
521 }
522}
523
524impl<T: Typed> Typed for Option<T> {
526 fn ty() -> Type {
527 T::ty() | Type::nil()
528 }
529
530 fn as_param() -> Type {
531 T::as_param() | Type::nil()
532 }
533
534 fn as_return() -> Type {
535 T::as_return() | Type::nil()
536 }
537}
538
539impl<T: Typed> Typed for Box<T> {
540 fn ty() -> Type {
541 T::ty()
542 }
543
544 fn as_param() -> Type {
545 T::as_param()
546 }
547
548 fn as_return() -> Type {
549 T::as_return()
550 }
551}
552
553impl<T: Typed> Typed for PhantomData<T> {
554 fn ty() -> Type {
555 T::ty()
556 }
557
558 fn as_param() -> Type {
559 T::as_param()
560 }
561
562 fn as_return() -> Type {
563 T::as_return()
564 }
565}
566
567impl<const N: usize> From<[Type; N]> for Type {
577 fn from(value: [Type; N]) -> Self {
578 Type::Tuple(Vec::from(value))
579 }
580}
581
582impl<I: Typed, const N: usize> Typed for [I; N] {
585 fn ty() -> Type {
586 Type::Array(I::ty().into())
587 }
588
589 fn as_param() -> Type {
590 Type::Array(I::as_param().into())
591 }
592
593 fn as_return() -> Type {
594 Type::Array(I::as_return().into())
595 }
596}
597
598impl<I: Typed> Typed for Vec<I> {
599 fn ty() -> Type {
600 Type::Array(I::ty().into())
601 }
602
603 fn as_param() -> Type {
604 Type::Array(I::as_param().into())
605 }
606
607 fn as_return() -> Type {
608 Type::Array(I::as_return().into())
609 }
610}
611
612impl<I: Typed> Typed for &[I] {
613 fn ty() -> Type {
614 Type::Array(I::ty().into())
615 }
616
617 fn as_param() -> Type {
618 Type::Array(I::as_param().into())
619 }
620
621 fn as_return() -> Type {
622 Type::Array(I::as_return().into())
623 }
624}
625
626impl<I: Typed> Typed for HashSet<I> {
627 fn ty() -> Type {
628 Type::Array(I::ty().into())
629 }
630
631 fn as_param() -> Type {
632 Type::Array(I::as_param().into())
633 }
634
635 fn as_return() -> Type {
636 Type::Array(I::as_return().into())
637 }
638}
639
640impl<I: Typed> Typed for BTreeSet<I> {
641 fn ty() -> Type {
642 Type::Array(I::ty().into())
643 }
644
645 fn as_param() -> Type {
646 Type::Array(I::as_param().into())
647 }
648
649 fn as_return() -> Type {
650 Type::Array(I::as_return().into())
651 }
652}
653
654impl<K, V> Typed for BTreeMap<K, V>
657where
658 K: Typed,
659 V: Typed,
660{
661 fn ty() -> Type {
662 Type::Map(K::ty().into(), V::ty().into())
663 }
664
665 fn as_param() -> Type {
666 Type::Map(K::as_param().into(), V::as_param().into())
667 }
668
669 fn as_return() -> Type {
670 Type::Map(K::as_return().into(), V::as_return().into())
671 }
672}
673
674impl<K, V> Typed for HashMap<K, V>
675where
676 K: Typed,
677 V: Typed,
678{
679 fn ty() -> Type {
680 Type::Map(K::ty().into(), V::ty().into())
681 }
682
683 fn as_param() -> Type {
684 Type::Map(K::as_param().into(), V::as_param().into())
685 }
686
687 fn as_return() -> Type {
688 Type::Map(K::as_return().into(), V::as_return().into())
689 }
690}
691
692#[cfg(feature="userdata-wrappers")]
695impl<T: Typed> Typed for Arc<T> {
696 fn ty() -> Type {
697 T::ty()
698 }
699
700 fn as_param() -> Type {
701 T::as_param()
702 }
703
704 fn as_return() -> Type {
705 T::as_return()
706 }
707}
708
709#[cfg(feature="userdata-wrappers")]
710impl<T: Typed> Typed for Rc<T> {
711 fn ty() -> Type {
712 T::ty()
713 }
714
715 fn as_param() -> Type {
716 T::as_param()
717 }
718
719 fn as_return() -> Type {
720 T::as_return()
721 }
722}
723
724#[cfg(feature="userdata-wrappers")]
725impl<T: Typed> Typed for Cell<T> {
726 fn ty() -> Type {
727 T::ty()
728 }
729
730 fn as_param() -> Type {
731 T::as_param()
732 }
733
734 fn as_return() -> Type {
735 T::as_return()
736 }
737}
738
739#[cfg(feature="userdata-wrappers")]
740impl<T: Typed> Typed for RefCell<T> {
741 fn ty() -> Type {
742 T::ty()
743 }
744
745 fn as_param() -> Type {
746 T::as_param()
747 }
748
749 fn as_return() -> Type {
750 T::as_return()
751 }
752}
753
754#[cfg(feature="userdata-wrappers")]
755impl<T: Typed> Typed for Mutex<T> {
756 fn ty() -> Type {
757 T::ty()
758 }
759
760 fn as_param() -> Type {
761 T::as_param()
762 }
763
764 fn as_return() -> Type {
765 T::as_return()
766 }
767}
768
769pub trait TypedMultiValue {
771 fn get_types() -> Vec<Type> {
774 Self::get_types_as_params()
775 .into_iter()
776 .map(|v| v.ty)
777 .collect::<Vec<_>>()
778 }
779
780 fn get_types_as_returns() -> Vec<Return> {
781 Vec::new()
782 }
783
784 fn get_types_as_params() -> Vec<Param> {
786 Vec::new()
787 }
788}
789
790macro_rules! impl_typed_multi_value {
791 () => {
792 impl TypedMultiValue for () {}
793 };
794 ($($name:ident) +) => {
795 impl<$($name,)* > TypedMultiValue for ($($name,)*)
796 where $($name: Typed,)*
797 {
798 #[allow(unused_mut)]
799 #[allow(non_snake_case)]
800 fn get_types_as_params() -> Vec<Param> {
801 Vec::from([
802 $(Param {
803 doc: None,
804 name: None,
805 ty: $name::as_param(),
806 },)*
807 ])
808 }
809
810 #[allow(unused_mut)]
811 #[allow(non_snake_case)]
812 fn get_types_as_returns() -> Vec<Return> {
813 Vec::from([
814 $(Return {
815 doc: None,
816 ty: $name::as_return(),
817 },)*
818 ])
819 }
820 }
821 };
822}
823
824impl<A> TypedMultiValue for A
825where
826 A: Typed,
827{
828 fn get_types_as_params() -> Vec<Param> {
829 Vec::from([Param { name: None, doc: None, ty: A::as_param()}])
830 }
831
832 fn get_types_as_returns() -> Vec<Return> {
833 Vec::from([Return { doc: None, ty: A::as_return() }])
834 }
835}
836
837impl_typed_multi_value!(A B C D E F G H I J K L M N O P);
839impl_typed_multi_value!(A B C D E F G H I J K L M N O);
840impl_typed_multi_value!(A B C D E F G H I J K L M N);
841impl_typed_multi_value!(A B C D E F G H I J K L M);
842impl_typed_multi_value!(A B C D E F G H I J K L);
843impl_typed_multi_value!(A B C D E F G H I J K);
844impl_typed_multi_value!(A B C D E F G H I J);
845impl_typed_multi_value!(A B C D E F G H I);
846impl_typed_multi_value!(A B C D E F G H);
847impl_typed_multi_value!(A B C D E F G);
848impl_typed_multi_value!(A B C D E F);
849impl_typed_multi_value!(A B C D E);
850impl_typed_multi_value!(A B C D);
851impl_typed_multi_value!(A B C);
852impl_typed_multi_value!(A B);
853impl_typed_multi_value!(A);
854impl_typed_multi_value!();
855
856#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
858pub struct Field {
859 pub ty: Type,
860 pub doc: Option<Cow<'static, str>>,
861}
862
863impl Field {
864 pub fn new(ty: Type, doc: impl IntoDocComment) -> Self {
865 Self {
866 ty,
867 doc: doc.into_doc_comment(),
868 }
869 }
870}
871
872#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
874pub struct StaticField {
875 pub inner: Field,
876 pub default: Cow<'static, str>,
877}
878
879impl StaticField {
880 pub fn new(ty: Type, doc: impl IntoDocComment, default: impl Into<Cow<'static, str>>) -> Self {
881 Self {
882 inner: Field {
883 ty,
884 doc: doc.into_doc_comment(),
885 },
886 default: default.into()
887 }
888 }
889}
890
891#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
893pub struct Func {
894 pub params: Vec<Param>,
895 pub returns: Vec<Return>,
896 pub doc: Option<Cow<'static, str>>,
897}
898
899impl Func {
900 pub fn new<Params, Returns>(
901 doc: impl IntoDocComment,
902 params: Vec<(Option<Type>, String, Option<Cow<'static, str>>)>,
903 returns: Vec<(Option<Type>, Option<Cow<'static, str>>)>,
904 ) -> Self
905 where
906 Params: TypedMultiValue,
907 Returns: TypedMultiValue,
908 {
909 Self {
910 params: Params::get_types_as_params()
911 .into_iter()
912 .enumerate()
913 .map(|(i, mut v)| {
914 if let Some((ty, name, doc)) = params.get(i) {
915 v.name(name.clone()).doc(doc.clone());
916 if let Some(t) = ty {
917 v.ty(t.clone());
918 }
919 }
920 v
921 })
922 .collect(),
923 returns: Returns::get_types_as_returns()
924 .into_iter()
925 .enumerate()
926 .map(|(i, mut v)| {
927 if let Some((ty, doc)) = returns.get(i) {
928 v.doc(doc.clone());
929 if let Some(t) = ty {
930 v.ty(t.clone());
931 }
932 }
933 v
934 })
935 .collect(),
936 doc: doc.into_doc_comment(),
937 }
938 }
939}
940
941pub trait IntoDocComment {
943 fn into_doc_comment(self) -> Option<Cow<'static, str>>;
944}
945
946impl IntoDocComment for String {
947 fn into_doc_comment(self) -> Option<Cow<'static, str>> {
948 (!self.trim().is_empty()).then_some(self.into())
949 }
950}
951
952impl IntoDocComment for &str {
953 fn into_doc_comment(self) -> Option<Cow<'static, str>> {
954 (!self.trim().is_empty()).then_some(self.to_string().into())
955 }
956}
957
958impl IntoDocComment for () {
959 fn into_doc_comment(self) -> Option<Cow<'static, str>> {
960 None
961 }
962}
963
964impl IntoDocComment for Option<String> {
965 fn into_doc_comment(self) -> Option<Cow<'static, str>> {
966 self.and_then(|v| (!v.trim().is_empty()).then_some(v.into()))
967 }
968}
969impl IntoDocComment for Option<Cow<'static, str>> {
970 fn into_doc_comment(self) -> Option<Cow<'static, str>> {
971 self.and_then(|v| (!v.trim().is_empty()).then_some(v))
972 }
973}