mlua_extras/extras/
mod.rs

1use std::path::Path;
2
3use mlua::{AnyUserData, FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Lua, Table, UserDataFields};
4
5mod macros;
6mod module;
7mod require;
8
9pub use module::{LuaModule, Module, ModuleBuilder, ModuleFields, ModuleMethods};
10pub use require::Require;
11
12use crate::MaybeSend;
13
14/// Adds quality of life helper methods to the [`Lua`] type
15///
16/// Helpers:
17/// - [`path`](https://www.lua.org/manual/5.1/manual.html#pdf-package.path) and [`cpath`](https://www.lua.org/manual/5.1/manual.html#pdf-package.cpath) manipulation
18/// - Shorthand for `lua.globals().set` that include adding any value and adding rust functions
19///     skipping [`create_function`][mlua::Lua::create_function]
20/// - A `require` method that is similar to the [`Require`] traits. Allows for `lua` style [`require`](https://www.lua.org/manual/5.1/manual.html#pdf-require)
21pub trait LuaExtras {
22    /// Get the `package.path` value
23    ///
24    /// This is the value used by the lua engine to resolve `require` calls on `lua` files.
25    /// see:
26    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.path>
27    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
28    fn path(&self) -> mlua::Result<String>;
29
30    /// Get the `package.cpath` value
31    ///
32    /// This is the value used by the lua engine to resolve `require` calls on `lib` files.
33    /// see:
34    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.cpath>
35    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
36    fn cpath(&self) -> mlua::Result<String>;
37
38    /// Prepend a path tothe `package.path` value
39    ///
40    /// This is the value used by the lua engine to resolve `require` calls.
41    /// see:
42    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.path>
43    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
44    fn prepend_path<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()>;
45
46    /// Prepend paths to the `package.path` value
47    ///
48    /// This is the value used by the lua engine to resolve `require` calls.
49    /// see:
50    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.path>
51    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
52    fn prepend_paths<S: AsRef<Path>>(&self, paths: impl IntoIterator<Item = S>)
53        -> mlua::Result<()>;
54
55    /// Append a path tothe `package.path` value
56    ///
57    /// This is the value used by the lua engine to resolve `require` calls.
58    /// see:
59    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.path>
60    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
61    fn append_path<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()>;
62
63    /// Append paths to the `package.path` value
64    ///
65    /// This is the value used by the lua engine to resolve `require` calls.
66    /// see:
67    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.path>
68    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
69    fn append_paths<S: AsRef<Path>>(&self, paths: impl IntoIterator<Item = S>) -> mlua::Result<()>;
70
71    /// Set the `package.path` value
72    ///
73    /// This is the value used by the lua engine to resolve `require` calls.
74    /// see:
75    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.path>
76    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
77    fn set_path<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()>;
78
79    /// Set the `package.path` values
80    ///
81    /// This is the value used by the lua engine to resolve `require` calls.
82    /// see:
83    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.path>
84    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
85    fn set_paths<S: AsRef<Path>>(&self, paths: impl IntoIterator<Item = S>) -> mlua::Result<()>;
86
87    /// Prepend a path tothe `package.cpath` value
88    ///
89    /// This is the value used by the lua engine to resolve `require` calls.
90    /// see:
91    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.cpath>
92    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
93    fn prepend_cpath<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()>;
94
95    /// Prepend paths to the `package.cpath` value
96    ///
97    /// This is the value used by the lua engine to resolve `require` calls.
98    /// see:
99    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.cpath>
100    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
101    fn prepend_cpaths<S: AsRef<Path>>(
102        &self,
103        paths: impl IntoIterator<Item = S>,
104    ) -> mlua::Result<()>;
105
106    /// Append a path to the `package.cpath` value
107    ///
108    /// This is the value used by the lua engine to resolve `require` calls.
109    /// see:
110    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.cpath>
111    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
112    fn append_cpath<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()>;
113
114    /// Append paths to the `package.cpath` value
115    ///
116    /// This is the value used by the lua engine to resolve `require` calls.
117    /// see:
118    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.cpath>
119    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
120    fn append_cpaths<S: AsRef<Path>>(&self, paths: impl IntoIterator<Item = S>)
121        -> mlua::Result<()>;
122
123    /// Set the `package.cpath` value
124    ///
125    /// This is the value used by the lua engine to resolve `require` calls.
126    /// see:
127    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.cpath>
128    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
129    fn set_cpath<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()>;
130
131    /// Set the `package.cpath` values
132    ///
133    /// This is the value used by the lua engine to resolve `require` calls.
134    /// see:
135    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.cpath>
136    ///   - <https://www.lua.org/manual/5.4/manual.html#pdf-package.searchpath>
137    fn set_cpaths<S: AsRef<Path>>(&self, paths: impl IntoIterator<Item = S>) -> mlua::Result<()>;
138
139    /// Set a global variable
140    fn set_global<'lua, K, V>(&'lua self, key: K, value: V) -> mlua::Result<()>
141    where
142        K: IntoLua<'lua>,
143        V: IntoLua<'lua>;
144
145    fn set_global_function<'lua, K, A, R, F>(&'lua self, key: K, value: F) -> mlua::Result<()>
146    where
147        K: IntoLua<'lua>,
148        A: FromLuaMulti<'lua>,
149        R: IntoLuaMulti<'lua>,
150        F: Fn(&'lua Lua, A) -> mlua::Result<R> + Send + 'static;
151
152    /// Fetch a nested lua value starting from lua's globals
153    fn require<'lua, R: FromLua<'lua>>(&'lua self, path: impl AsRef<str>) -> mlua::Result<R>;
154}
155
156impl LuaExtras for Lua {
157    fn set_global<'lua, K, V>(&'lua self, key: K, value: V) -> mlua::Result<()>
158    where
159        K: IntoLua<'lua>,
160        V: IntoLua<'lua>,
161    {
162        self.globals().set(key, value)
163    }
164
165    fn set_global_function<'lua, K, A, R, F>(&'lua self, key: K, value: F) -> mlua::Result<()>
166    where
167        K: IntoLua<'lua>,
168        A: FromLuaMulti<'lua>,
169        R: IntoLuaMulti<'lua>,
170        F: Fn(&'lua Lua, A) -> mlua::Result<R> + Send + 'static,
171    {
172        self.globals().set(key, self.create_function(value)?)
173    }
174
175    fn path(&self) -> mlua::Result<String> {
176        self.globals()
177            .get::<_, Table>("package")?
178            .get::<_, String>("path")
179    }
180
181    fn cpath(&self) -> mlua::Result<String> {
182        self.globals()
183            .get::<_, Table>("package")?
184            .get::<_, String>("cpath")
185    }
186
187    fn set_path<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()> {
188        self.globals()
189            .get::<_, Table>("package")
190            .unwrap()
191            .set("path", path.as_ref().display().to_string())
192    }
193
194    fn set_paths<S: AsRef<Path>>(&self, paths: impl IntoIterator<Item = S>) -> mlua::Result<()> {
195        self.globals().get::<_, Table>("package").unwrap().set(
196            "path",
197            paths
198                .into_iter()
199                .map(|s| s.as_ref().display().to_string())
200                .collect::<Vec<_>>()
201                .join(";"),
202        )
203    }
204
205    fn prepend_path<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()> {
206        let lua_path = match self.path()?.trim() {
207            "" => path.as_ref().display().to_string(),
208            other => format!("{};{other}", path.as_ref().display()),
209        };
210        self.globals()
211            .get::<_, Table>("package")?
212            .set("path", lua_path)
213    }
214
215    fn prepend_paths<S: AsRef<Path>>(
216        &self,
217        paths: impl IntoIterator<Item = S>,
218    ) -> mlua::Result<()> {
219        let new = paths
220            .into_iter()
221            .map(|v| v.as_ref().display().to_string())
222            .collect::<Vec<_>>()
223            .join(";");
224        let lua_path = match self.path()?.trim() {
225            "" => new,
226            other => format!("{new};{other}"),
227        };
228        self.globals()
229            .get::<_, Table>("package")?
230            .set("path", lua_path)
231    }
232
233    fn append_path<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()> {
234        let lua_path = match self.path()?.trim() {
235            "" => path.as_ref().display().to_string(),
236            other => format!("{other};{}", path.as_ref().display()),
237        };
238        self.globals()
239            .get::<_, Table>("package")?
240            .set("path", lua_path)
241    }
242
243    fn append_paths<S: AsRef<Path>>(&self, paths: impl IntoIterator<Item = S>) -> mlua::Result<()> {
244        let new = paths
245            .into_iter()
246            .map(|v| v.as_ref().display().to_string())
247            .collect::<Vec<_>>()
248            .join(";");
249        let lua_path = match self.path()?.trim() {
250            "" => new,
251            other => format!("{other};{new}"),
252        };
253        self.globals()
254            .get::<_, Table>("package")?
255            .set("path", lua_path)
256    }
257
258    fn set_cpath<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()> {
259        self.globals()
260            .get::<_, Table>("package")
261            .unwrap()
262            .set("cpath", path.as_ref().display().to_string())
263    }
264
265    fn set_cpaths<S: AsRef<Path>>(&self, paths: impl IntoIterator<Item = S>) -> mlua::Result<()> {
266        self.globals().get::<_, Table>("package").unwrap().set(
267            "cpath",
268            paths
269                .into_iter()
270                .map(|s| s.as_ref().display().to_string())
271                .collect::<Vec<_>>()
272                .join(";"),
273        )
274    }
275
276    fn prepend_cpath<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()> {
277        let lua_path = match self.path()?.trim() {
278            "" => path.as_ref().display().to_string(),
279            other => format!("{};{other}", path.as_ref().display()),
280        };
281        self.globals()
282            .get::<_, Table>("package")?
283            .set("cpath", lua_path)
284    }
285
286    fn prepend_cpaths<S: AsRef<Path>>(
287        &self,
288        paths: impl IntoIterator<Item = S>,
289    ) -> mlua::Result<()> {
290        let new = paths
291            .into_iter()
292            .map(|v| v.as_ref().display().to_string())
293            .collect::<Vec<_>>()
294            .join(";");
295        let lua_path = match self.path()?.trim() {
296            "" => new,
297            other => format!("{new};{other}"),
298        };
299        self.globals()
300            .get::<_, Table>("package")?
301            .set("cpath", lua_path)
302    }
303
304    fn append_cpath<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()> {
305        let lua_path = match self.path()?.trim() {
306            "" => path.as_ref().display().to_string(),
307            other => format!("{other};{}", path.as_ref().display()),
308        };
309        self.globals()
310            .get::<_, Table>("package")?
311            .set("cpath", lua_path)
312    }
313
314    fn append_cpaths<S: AsRef<Path>>(
315        &self,
316        paths: impl IntoIterator<Item = S>,
317    ) -> mlua::Result<()> {
318        let new = paths
319            .into_iter()
320            .map(|v| v.as_ref().display().to_string())
321            .collect::<Vec<_>>()
322            .join(";");
323        let lua_path = match self.path()?.trim() {
324            "" => new,
325            other => format!("{other};{new}"),
326        };
327        self.globals()
328            .get::<_, Table>("package")?
329            .set("cpath", lua_path)
330    }
331
332    fn require<'lua, R: FromLua<'lua>>(&'lua self, path: impl AsRef<str>) -> mlua::Result<R> {
333        let segments = path
334            .as_ref()
335            .split('.')
336            .filter_map(|v| (!v.is_empty()).then_some(v.trim()))
337            .collect::<Vec<_>>();
338
339        let mut module = self.globals();
340        if !segments.is_empty() {
341            for seg in &segments[..segments.len() - 1] {
342                module = module.get::<_, Table>(*seg)?;
343            }
344        }
345
346        match segments.last() {
347            Some(seg) => module.get::<_, R>(*seg),
348            None => Err(mlua::Error::runtime(format!(
349                "module not found: {:?}",
350                path.as_ref()
351            ))),
352        }
353    }
354}
355
356/// Helper that combines some of the assignments of fields for UserData
357pub trait UserDataGetSet<'lua, T> {
358    /// Combination of [add_field_method_get](mlua::UserDataFields::add_field_method_get) and [add_field_method_set](mlua::UserDataFields::add_field_method_set)
359    fn add_field_method_get_set<S, R, A, GET, SET>(&mut self, name: &S, get: GET, set: SET)
360    where
361        S: AsRef<str> + ?Sized,
362        R: IntoLua<'lua>,
363        A: FromLua<'lua>,
364        GET: 'static + MaybeSend + Fn(&'lua Lua, &T) -> mlua::Result<R>,
365        SET: 'static + MaybeSend + Fn(&'lua Lua, &mut T, A) -> mlua::Result<()>;
366
367    /// Typed version of [add_field_function_get](mlua::UserDataFields::add_field_function_get) and [add_field_function_set](mlua::UserDataFields::add_field_function_set) combined
368    fn add_field_function_get_set<S, R, A, GET, SET>(&mut self, name: &S, get: GET, set: SET)
369    where
370        S: AsRef<str> + ?Sized,
371        R: IntoLua<'lua>,
372        A: FromLua<'lua>,
373        GET: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> mlua::Result<R>,
374        SET: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>, A) -> mlua::Result<()>;
375}
376
377impl<'lua, T, U: UserDataFields<'lua, T>> UserDataGetSet<'lua, T> for U {
378    fn add_field_method_get_set<S, R, A, GET, SET>(&mut self, name: &S, get: GET, set: SET)
379    where
380        S: AsRef<str> + ?Sized,
381        R: IntoLua<'lua>,
382        A: FromLua<'lua>,
383        GET: 'static + MaybeSend + Fn(&'lua Lua, &T) -> mlua::Result<R>,
384        SET: 'static + MaybeSend + Fn(&'lua Lua, &mut T, A) -> mlua::Result<()>,
385    {
386        self.add_field_method_get(name, get);
387        self.add_field_method_set(name, set);
388    }
389
390    fn add_field_function_get_set<S, R, A, GET, SET>(&mut self, name: &S, get: GET, set: SET)
391    where
392        S: AsRef<str> + ?Sized,
393        R: IntoLua<'lua>,
394        A: FromLua<'lua>,
395        GET: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> mlua::Result<R>,
396        SET: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>, A) -> mlua::Result<()>,
397    {
398        self.add_field_function_get(name, get);
399        self.add_field_function_set(name, set);
400    }
401}