Skip to main content

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