1#![allow(clippy::cargo_common_metadata)]
2
3use std::time::{Duration, Instant};
4
5use async_io::Timer;
6use futures_lite::future::yield_now;
7
8use mlua::prelude::*;
9use mlua_luau_scheduler::Functions;
10
11use lune_utils::TableBuilder;
12
13const TYPEDEFS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/types.d.luau"));
14
15#[must_use]
19pub fn typedefs() -> String {
20 TYPEDEFS.to_string()
21}
22
23pub fn module(lua: Lua) -> LuaResult<LuaTable> {
31 let fns = Functions::new(lua.clone())?;
32
33 let task_wait = lua.create_async_function(wait)?;
35 let task_delay_env = TableBuilder::new(lua.clone())?
36 .with_value("select", lua.globals().get::<LuaFunction>("select")?)?
37 .with_value("spawn", fns.spawn.clone())?
38 .with_value("defer", fns.defer.clone())?
39 .with_value("wait", task_wait.clone())?
40 .build_readonly()?;
41 let task_delay = lua
42 .load(DELAY_IMPL_LUA)
43 .set_name("task.delay")
44 .set_environment(task_delay_env)
45 .into_function()?;
46
47 TableBuilder::new(lua)?
48 .with_value("cancel", fns.cancel)?
49 .with_value("defer", fns.defer)?
50 .with_value("delay", task_delay)?
51 .with_value("spawn", fns.spawn)?
52 .with_value("wait", task_wait)?
53 .build_readonly()
54}
55
56const DELAY_IMPL_LUA: &str = r"
57return defer(function(...)
58 wait(select(1, ...))
59 spawn(select(2, ...))
60end, ...)
61";
62
63async fn wait(lua: Lua, secs: Option<f64>) -> LuaResult<f64> {
64 yield_now().await;
67 wait_inner(lua, secs).await
68}
69
70async fn wait_inner(_: Lua, secs: Option<f64>) -> LuaResult<f64> {
71 let duration = Duration::from_secs_f64(secs.unwrap_or_default());
75 let duration = duration.max(Duration::from_millis(1));
76 yield_now().await;
80 let before = Instant::now();
82 let after = Timer::after(duration).await;
83 Ok((after - before).as_secs_f64())
84}