Skip to main content

mlua_extras/typed/
mod.rs

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/// Represents a lua table key
22///
23/// Table keys can be either a string or an integer
24#[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/// Representation of a lua type for a rust type
85#[derive(Debug, Clone, PartialEq, strum::AsRefStr, strum::EnumIs, PartialOrd, Eq, Ord, Hash)]
86pub enum Type {
87    /// Represents a single type. i.e. `string`, `number`, `0`, `"literal"`, `Example`, etc...
88    ///
89    /// # Example
90    ///
91    /// ```lua
92    /// --- @type string
93    /// --- @type number
94    /// --- @type 0
95    /// --- @type "literal"
96    /// --- @type Example
97    /// ```
98    Single(Cow<'static, str>),
99    /// Represents a typed value
100    ///
101    /// # Example
102    ///
103    /// ```lua
104    /// --- @type {type}
105    /// value = nil
106    /// ```
107    Value(Box<Type>),
108    /// Represents a type alias
109    ///
110    /// # Example
111    ///
112    /// ```lua
113    /// --- @alias MyType {type}
114    /// ```
115    Alias(Box<Type>),
116    /// Represents a tuple type
117    ///
118    /// # Example
119    ///
120    /// ```lua
121    /// --- @type [number, integer, string]
122    /// ```
123    Tuple(Vec<Type>),
124    /// Represents a table literal
125    ///
126    /// # Example
127    ///
128    /// ```lua
129    /// --- @type { name: string, age: integer, height: number }
130    /// ```
131    Table(BTreeMap<Index, Type>),
132    /// Represents a type union
133    ///
134    /// # Example
135    ///
136    /// ```lua
137    /// --- @type string | number | "literal" | Example
138    /// ```
139    Union(Vec<Type>),
140    /// Represents an array of a single type
141    ///
142    /// # Example
143    /// ```lua
144    /// --- @type string[]
145    /// ```
146    Array(Box<Type>),
147    /// Represents a table with set key types and value types
148    ///
149    /// # Example
150    ///
151    /// ```lua
152    /// --- @type { [string]: boolean }
153    /// ```
154    Map(Box<Type>, Box<Type>),
155    /// Represents a function with it's parameters and return types
156    ///
157    /// # Example
158    ///
159    /// ```lua
160    /// --- @type fun(self: any, name: string): string
161    /// ```
162    Function {
163        params: Vec<Param>,
164        returns: Vec<Return>,
165    },
166    /// References a enum type.
167    ///
168    /// In this instance it acts like a union since that is the closest relation between rust
169    /// enums and a lua type
170    ///
171    /// # Example
172    ///
173    /// ```lua
174    /// --- @alias {name} {type}
175    /// ---  | {type}
176    /// ---  | {type}
177    /// ---  | {type}
178    /// ```
179    Enum(Vec<Type>),
180    /// Represents a class type
181    ///
182    /// # Example
183    ///
184    /// ```lua
185    /// --- @class {name}
186    /// --- @field name string
187    /// --- @field age integer
188    /// --- @field height number
189    /// ```
190    Class(Box<TypedClass>),
191}
192
193/// Allows to union types
194///
195/// # Example
196///
197/// ```
198/// use mlua_extras::typed::Type;
199///
200/// Type::string() | Type::nil();
201/// ```
202impl<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    /// Create a lua type literal from a rust value. i.e. `3`, `true`, etc...
234    pub fn literal<T: IntoLuaTypeLiteral>(value: T) -> Self {
235        Self::Single(value.into_lua_type_literal().into())
236    }
237
238    /// Create a reference to a name of another defined type.
239    ///
240    /// # Example
241    ///
242    /// ```lua
243    /// --- @class Example
244    ///
245    /// --- @type Example
246    /// example = nil
247    /// ```
248    ///
249    /// ```
250    /// use mlua_extras::typed::Type;
251    ///
252    /// // This reference the type named `Example`
253    /// Type::named("Example");
254    /// ```
255    pub fn named(value: impl Into<Cow<'static, str>>) -> Self {
256        Self::Single(value.into())
257    }
258
259    /// Create a lua builtin `string` type
260    pub fn string() -> Self {
261        Self::Single("string".into())
262    }
263
264    /// Create a lua builtin `integer` type
265    pub fn integer() -> Self {
266        Self::Single("integer".into())
267    }
268
269    /// Create a lua builtin `number` type
270    pub fn number() -> Self {
271        Self::Single("number".into())
272    }
273
274    /// Create a lua builtin `boolean` type
275    pub fn boolean() -> Self {
276        Self::Single("boolean".into())
277    }
278
279    /// Create a lua builtin `nil` type
280    pub fn nil() -> Self {
281        Self::Single("nil".into())
282    }
283
284    /// Create a lua builtin `any` type
285    pub fn any() -> Self {
286        Self::Single("any".into())
287    }
288
289    /// Create a lua builtin `lightuserdata` type
290    pub fn lightuserdata() -> Self {
291        Self::Single("lightuserdata".into())
292    }
293
294    /// Create a lua builtin `thread` type
295    pub fn thread() -> Self {
296        Self::Single("thread".into())
297    }
298
299    /// Create an enum type. This is equal to an [`alias`][crate::typed::Type::Alias]
300    pub fn r#enum(types: impl IntoIterator<Item = Type>) -> Self {
301        Self::Enum(types.into_iter().collect())
302    }
303
304    /// Create a type that is an alias. i.e. `--- @alias {name} string`
305    pub fn alias(ty: Type) -> Self {
306        Self::Alias(Box::new(ty))
307    }
308
309    /// Create a type that is an array. i.e. `{ [integer]: type }`
310    pub fn array(ty: Type) -> Self {
311        Self::Array(Box::new(ty))
312    }
313
314    /// Create a type that is an array. i.e. `{ [integer]: type }`
315    pub fn map(key: Type, value: Type) -> Self {
316        Self::Map(Box::new(key), Box::new(value))
317    }
318
319    /// Create a type that is a union. i.e. `string | integer | nil`
320    pub fn union(types: impl IntoIterator<Item = Type>) -> Self {
321        Self::Union(types.into_iter().collect())
322    }
323
324    /// create a type that is a tuple. i.e. `{ [1]: type, [2]: type }`
325    pub fn tuple(types: impl IntoIterator<Item = Type>) -> Self {
326        Self::Tuple(types.into_iter().collect())
327    }
328
329    /// create a type that is a class. i.e. `--- @class {name}`
330    pub fn class(class: TypedClass) -> Self {
331        Self::Class(Box::new(class))
332    }
333
334    /// create a type that is a function. i.e. `fun(self): number`
335    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    /// A table that has defined entries.
364    ///
365    /// If the goal is a map like syntax use [`Type::Map`] or [`Type::map`] instead
366    pub fn table(items: impl IntoIterator<Item = (Index, Type)>) -> Self {
367        Self::Table(items.into_iter().collect())
368    }
369}
370
371pub trait IntoLuaTypeLiteral {
372    /// Construct the representation of the value as a lua type
373    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
418/// Add a lua [`Type`] representation to a rust type
419pub trait Typed {
420    /// Get the type representation
421    fn ty() -> Type;
422
423    #[inline(always)]
424    fn implicit() -> impl IntoIterator<Item = (&'static str, Type)> {
425        []
426    }
427
428    /// Get the type as a function parameter
429    fn as_param() -> Type {
430        Self::ty()
431    }
432
433    /// Get the type as a function return
434    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    /// ...type
513    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
524/// {type} | nil
525impl<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
567// Represents a lua tuple.
568//
569// With luaCATS tuples are represented with square brackets.
570//
571// # Example
572//
573// ```lua
574// --- @type [string, integer, "literal"]
575// ```
576impl<const N: usize> From<[Type; N]> for Type {
577    fn from(value: [Type; N]) -> Self {
578        Type::Tuple(Vec::from(value))
579    }
580}
581
582// Array type
583
584impl<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
654// Map type
655
656impl<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// External Types
693
694#[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
769/// Typed information for a lua [`MultiValue`][mlua::MultiValue]
770pub trait TypedMultiValue {
771    /// Gets the types contained in this collection.
772    /// Order *IS* important.
773    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    /// Gets the type representations as used for function parameters
785    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
837// TODO: Replace count argument with ${count($name)} when meta-variables are stable
838impl_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/// Type information for a lua `class` field
857#[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/// Type information for a lua `class` field
873#[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/// Type information for a lua `class` function
892#[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
941/// Helper that converts multiple different types into an `Option<Cow<'static, str>>`
942pub 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}