mlua_extras/typed/
function.rs

1use std::{borrow::Cow, marker::PhantomData};
2
3use mlua::{FromLua, FromLuaMulti, Function, IntoLua, IntoLuaMulti, Lua, Value};
4
5use crate::MaybeSend;
6
7use super::{IntoDocComment, Type, Typed, TypedMultiValue};
8
9/// A function parameter type representation
10#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
11pub struct Param {
12    pub doc: Option<Cow<'static, str>>,
13    ///If the parameter has a name (will default to Param{number} if None)
14    pub name: Option<Cow<'static, str>>,
15    ///The type of the parameter
16    pub ty: Type,
17}
18
19impl Param {
20    /// Set the parameters name
21    pub fn name(&mut self, name: impl Into<Cow<'static, str>>) -> &mut Self {
22        self.name = Some(name.into());
23        self
24    }
25
26    /// Set the parameters doc comment
27    pub fn doc(&mut self, doc: impl IntoDocComment) -> &mut Self {
28        self.doc = doc.into_doc_comment();
29        self
30    }
31
32    /// Set the parameters type manually
33    pub fn ty(&mut self, ty: Type) -> &mut Self {
34        self.ty = ty;
35        self
36    }
37}
38
39/// A function parameter type representation
40#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
41pub struct Return {
42    pub doc: Option<Cow<'static, str>>,
43    ///The type of the return
44    pub ty: Type,
45}
46
47impl Return {
48    /// Set the parameters doc comment
49    pub fn doc(&mut self, doc: impl IntoDocComment) -> &mut Self {
50        self.doc = doc.into_doc_comment();
51        self
52    }
53
54    /// Set the parameters doc comment
55    pub fn ty(&mut self, ty: Type) -> &mut Self {
56        self.ty = ty;
57        self
58    }
59}
60
61impl<I: Into<Cow<'static, str>>> From<(I, Type)> for Param {
62    fn from((name, ty): (I, Type)) -> Self {
63        Param {
64            doc: None,
65            name: Some(name.into()),
66            ty,
67        }
68    }
69}
70
71impl From<Type> for Param {
72    fn from(value: Type) -> Self {
73        Param {
74            doc: None,
75            name: None,
76            ty: value,
77        }
78    }
79}
80
81/// Used to purely get function type information without converting it to anything
82/// else.
83pub trait IntoTypedFunction<'lua, Params: TypedMultiValue, Response: TypedMultiValue> {
84    fn into_typed_function(
85        self,
86        lua: &'lua Lua,
87    ) -> mlua::Result<TypedFunction<'lua, Params, Response>>;
88}
89
90impl<'lua, F, Params, Response> IntoTypedFunction<'lua, Params, Response> for F
91where
92    Params: TypedMultiValue + FromLuaMulti<'lua>,
93    Response: TypedMultiValue + IntoLuaMulti<'lua>,
94    F: Fn(&'lua Lua, Params) -> mlua::Result<Response> + MaybeSend + 'static,
95{
96    fn into_typed_function(
97        self,
98        lua: &'lua Lua,
99    ) -> mlua::Result<TypedFunction<'lua, Params, Response>> {
100        Ok(TypedFunction {
101            inner: lua.create_function(self)?,
102            _p: PhantomData,
103            _r: PhantomData,
104        })
105    }
106}
107
108impl<'lua, Params, Response> IntoTypedFunction<'lua, Params, Response> for Function<'lua>
109where
110    Params: TypedMultiValue + FromLuaMulti<'lua>,
111    Response: TypedMultiValue + IntoLuaMulti<'lua>,
112{
113    fn into_typed_function(
114        self,
115        _lua: &'lua Lua,
116    ) -> mlua::Result<TypedFunction<'lua, Params, Response>> {
117        Ok(TypedFunction {
118            inner: self,
119            _p: PhantomData,
120            _r: PhantomData,
121        })
122    }
123}
124
125impl<'lua, Params, Response> IntoTypedFunction<'lua, Params, Response>
126    for &TypedFunction<'lua, Params, Response>
127where
128    Params: TypedMultiValue + FromLuaMulti<'lua>,
129    Response: TypedMultiValue + IntoLuaMulti<'lua>,
130{
131    fn into_typed_function(
132        self,
133        _lua: &'lua Lua,
134    ) -> mlua::Result<TypedFunction<'lua, Params, Response>> {
135        Ok(TypedFunction {
136            inner: self.inner.clone(),
137            _p: PhantomData,
138            _r: PhantomData,
139        })
140    }
141}
142
143impl<'lua, Params, Response> IntoTypedFunction<'lua, Params, Response> for ()
144where
145    Params: TypedMultiValue + FromLuaMulti<'lua>,
146    Response: TypedMultiValue + IntoLuaMulti<'lua>,
147{
148    fn into_typed_function(
149        self,
150        lua: &'lua Lua,
151    ) -> mlua::Result<TypedFunction<'lua, Params, Response>> {
152        Ok(TypedFunction {
153            inner: lua.create_function(|_, _: Params| Ok(()))?,
154            _p: PhantomData,
155            _r: PhantomData,
156        })
157    }
158}
159
160/// Helper to bake the type information for a lua [`Function`][mlua::Function]. This makes repeated
161/// calls to the [`Function`][mlua::Function]'s [`call`][mlua::Function::call] all the same with
162/// enforced arguments and return types.
163pub struct TypedFunction<'lua, Params, Response>
164where
165    Params: TypedMultiValue,
166    Response: TypedMultiValue,
167{
168    inner: Function<'lua>,
169    _p: PhantomData<Params>,
170    _r: PhantomData<Response>,
171}
172
173impl<'lua, Params, Response> TypedFunction<'lua, Params, Response>
174where
175    Params: TypedMultiValue + IntoLuaMulti<'lua>,
176    Response: TypedMultiValue + FromLuaMulti<'lua>,
177{
178    /// Same as [Function::call] but with the param and return
179    /// types already specified
180    pub fn call(&self, params: Params) -> mlua::Result<Response> {
181        self.inner.call::<Params, Response>(params)
182    }
183
184    /// Same as [Function::call] but with the param and return
185    /// types already specified
186    ///
187    /// # Safety
188    ///
189    /// Panics if any lua errors occur
190    pub unsafe fn call_unsafe(&self, params: Params) -> Response {
191        self.inner.call::<Params, Response>(params).unwrap()
192    }
193
194    /// Create a typed function from a rust function.
195    ///
196    /// This will call [`Lua::create_function`] under the hood
197    pub fn from_rust<F>(&self, lua: &'lua Lua, func: F) -> mlua::Result<Self>
198    where
199        Params: TypedMultiValue + FromLuaMulti<'lua>,
200        Response: TypedMultiValue + IntoLuaMulti<'lua>,
201        F: Fn(&'lua Lua, Params) -> mlua::Result<Response> + MaybeSend + 'static,
202    {
203        Ok(Self {
204            inner: lua.create_function(func)?,
205            _p: PhantomData,
206            _r: PhantomData,
207        })
208    }
209}
210
211impl<'lua, Params, Response> FromLua<'lua> for TypedFunction<'lua, Params, Response>
212where
213    Params: TypedMultiValue,
214    Response: TypedMultiValue,
215{
216    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> mlua::prelude::LuaResult<Self> {
217        Ok(Self {
218            inner: FromLua::from_lua(value, lua)?,
219            _p: PhantomData,
220            _r: PhantomData,
221        })
222    }
223}
224
225impl<'lua, Params, Response> IntoLua<'lua> for TypedFunction<'lua, Params, Response>
226where
227    Params: TypedMultiValue,
228    Response: TypedMultiValue,
229{
230    fn into_lua(self, _lua: &'lua Lua) -> mlua::prelude::LuaResult<Value<'lua>> {
231        Ok(Value::Function(self.inner))
232    }
233}
234
235impl<'lua, Params, Response> Typed for TypedFunction<'lua, Params, Response>
236where
237    Params: TypedMultiValue,
238    Response: TypedMultiValue,
239{
240    fn ty() -> Type {
241        Type::Function {
242            params: Params::get_types_as_params(),
243            returns: Response::get_types()
244                .into_iter()
245                .map(|ty| Return { doc: None, ty })
246                .collect(),
247        }
248    }
249}