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
14pub trait LuaExtras {
22    fn path(&self) -> mlua::Result<String>;
29
30    fn cpath(&self) -> mlua::Result<String>;
37
38    fn prepend_path<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()>;
45
46    fn prepend_paths<S: AsRef<Path>>(&self, paths: impl IntoIterator<Item = S>)
53        -> mlua::Result<()>;
54
55    fn append_path<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()>;
62
63    fn append_paths<S: AsRef<Path>>(&self, paths: impl IntoIterator<Item = S>) -> mlua::Result<()>;
70
71    fn set_path<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()>;
78
79    fn set_paths<S: AsRef<Path>>(&self, paths: impl IntoIterator<Item = S>) -> mlua::Result<()>;
86
87    fn prepend_cpath<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()>;
94
95    fn prepend_cpaths<S: AsRef<Path>>(
102        &self,
103        paths: impl IntoIterator<Item = S>,
104    ) -> mlua::Result<()>;
105
106    fn append_cpath<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()>;
113
114    fn append_cpaths<S: AsRef<Path>>(&self, paths: impl IntoIterator<Item = S>)
121        -> mlua::Result<()>;
122
123    fn set_cpath<S: AsRef<Path>>(&self, path: S) -> mlua::Result<()>;
130
131    fn set_cpaths<S: AsRef<Path>>(&self, paths: impl IntoIterator<Item = S>) -> mlua::Result<()>;
138
139    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    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
356pub trait UserDataGetSet<'lua, T> {
358    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    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}