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