mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Initial implementation of builtin libraries, task library
This commit is contained in:
parent
7a63987cbe
commit
0757d6f293
7 changed files with 234 additions and 8 deletions
44
src/lune/builtins/mod.rs
Normal file
44
src/lune/builtins/mod.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
mod task;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub enum LuneBuiltin {
|
||||
Task,
|
||||
}
|
||||
|
||||
impl<'lua> LuneBuiltin
|
||||
where
|
||||
'lua: 'static, // FIXME: Remove static lifetime bound here when builtin libraries no longer need it
|
||||
{
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Task => "task",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(&self, lua: &'lua Lua) -> LuaResult<LuaMultiValue<'lua>> {
|
||||
let res = match self {
|
||||
Self::Task => task::create(lua),
|
||||
};
|
||||
match res {
|
||||
Ok(v) => Ok(v.into_lua_multi(lua)?),
|
||||
Err(e) => Err(e.context(format!(
|
||||
"Failed to create builtin library '{}'",
|
||||
self.name()
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for LuneBuiltin {
|
||||
type Err = String;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.trim().to_ascii_lowercase().as_str() {
|
||||
"task" => Ok(Self::Task),
|
||||
_ => Err(format!("Unknown builtin library '{s}'")),
|
||||
}
|
||||
}
|
||||
}
|
93
src/lune/builtins/task/mod.rs
Normal file
93
src/lune/builtins/task/mod.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
use tokio::time::{self, Instant};
|
||||
|
||||
use crate::lune::{scheduler::Scheduler, util::TableBuilder};
|
||||
|
||||
mod tof;
|
||||
use tof::LuaThreadOrFunction;
|
||||
|
||||
pub fn create(lua: &'static Lua) -> LuaResult<impl IntoLuaMulti<'_>> {
|
||||
TableBuilder::new(lua)?
|
||||
.with_function("cancel", task_cancel)?
|
||||
.with_function("defer", task_defer)?
|
||||
.with_function("delay", task_delay)?
|
||||
.with_function("spawn", task_spawn)?
|
||||
.with_async_function("wait", task_wait)?
|
||||
.build_readonly()
|
||||
}
|
||||
|
||||
fn task_cancel(lua: &Lua, thread: LuaThread) -> LuaResult<()> {
|
||||
let close = lua
|
||||
.globals()
|
||||
.get::<_, LuaTable>("coroutine")?
|
||||
.get::<_, LuaFunction>("close")?;
|
||||
match close.call(thread) {
|
||||
Err(LuaError::CoroutineInactive) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
Ok(()) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn task_defer<'lua>(
|
||||
lua: &'lua Lua,
|
||||
(tof, args): (LuaThreadOrFunction<'lua>, LuaMultiValue<'_>),
|
||||
) -> LuaResult<LuaThread<'lua>> {
|
||||
let thread = tof.into_thread(lua)?;
|
||||
let sched = lua
|
||||
.app_data_ref::<&Scheduler>()
|
||||
.expect("Lua struct is missing scheduler");
|
||||
sched.push_back(thread.clone(), args)?;
|
||||
Ok(thread)
|
||||
}
|
||||
|
||||
// FIXME: `self` escapes outside of method because we are borrowing `tof` and
|
||||
// `args` when we call `schedule_future_thread` in the lua function body below
|
||||
// For now we solve this by using the 'static lifetime bound in the impl
|
||||
fn task_delay<'lua>(
|
||||
lua: &'lua Lua,
|
||||
(secs, tof, args): (f64, LuaThreadOrFunction<'lua>, LuaMultiValue<'lua>),
|
||||
) -> LuaResult<LuaThread<'lua>>
|
||||
where
|
||||
'lua: 'static,
|
||||
{
|
||||
let thread = tof.into_thread(lua)?;
|
||||
let sched = lua
|
||||
.app_data_ref::<&Scheduler>()
|
||||
.expect("Lua struct is missing scheduler");
|
||||
|
||||
let thread2 = thread.clone();
|
||||
sched.schedule_future_thread(thread.clone(), async move {
|
||||
let duration = Duration::from_secs_f64(secs);
|
||||
time::sleep(duration).await;
|
||||
sched.push_back(thread2, args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(thread)
|
||||
}
|
||||
|
||||
fn task_spawn<'lua>(
|
||||
lua: &'lua Lua,
|
||||
(tof, args): (LuaThreadOrFunction<'lua>, LuaMultiValue<'_>),
|
||||
) -> LuaResult<LuaThread<'lua>> {
|
||||
let thread = tof.into_thread(lua)?;
|
||||
let resume = lua
|
||||
.globals()
|
||||
.get::<_, LuaTable>("coroutine")?
|
||||
.get::<_, LuaFunction>("resume")?;
|
||||
resume.call((thread.clone(), args))?;
|
||||
Ok(thread)
|
||||
}
|
||||
|
||||
async fn task_wait(_: &Lua, secs: Option<f64>) -> LuaResult<f64> {
|
||||
let duration = Duration::from_secs_f64(secs.unwrap_or_default());
|
||||
|
||||
let before = Instant::now();
|
||||
time::sleep(duration).await;
|
||||
let after = Instant::now();
|
||||
|
||||
Ok((after - before).as_secs_f64())
|
||||
}
|
30
src/lune/builtins/task/tof.rs
Normal file
30
src/lune/builtins/task/tof.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) enum LuaThreadOrFunction<'lua> {
|
||||
Thread(LuaThread<'lua>),
|
||||
Function(LuaFunction<'lua>),
|
||||
}
|
||||
|
||||
impl<'lua> LuaThreadOrFunction<'lua> {
|
||||
pub(super) fn into_thread(self, lua: &'lua Lua) -> LuaResult<LuaThread<'lua>> {
|
||||
match self {
|
||||
Self::Thread(t) => Ok(t),
|
||||
Self::Function(f) => lua.create_thread(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for LuaThreadOrFunction<'lua> {
|
||||
fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<Self> {
|
||||
match value {
|
||||
LuaValue::Thread(t) => Ok(Self::Thread(t)),
|
||||
LuaValue::Function(f) => Ok(Self::Function(f)),
|
||||
value => Err(LuaError::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "LuaThreadOrFunction",
|
||||
message: Some("Expected thread or function".to_string()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,14 +3,13 @@ use mlua::prelude::*;
|
|||
use super::context::*;
|
||||
|
||||
pub(super) async fn require<'lua, 'ctx>(
|
||||
_lua: &'lua Lua,
|
||||
_ctx: &'ctx RequireContext,
|
||||
lua: &'lua Lua,
|
||||
ctx: &'ctx RequireContext,
|
||||
name: &str,
|
||||
) -> LuaResult<LuaMultiValue<'lua>>
|
||||
where
|
||||
'lua: 'ctx,
|
||||
'lua: 'static, // FIXME: Remove static lifetime bound here when builtin libraries no longer need it
|
||||
{
|
||||
Err(LuaError::runtime(format!(
|
||||
"TODO: Support require for built-in libraries (tried to require '{name}')"
|
||||
)))
|
||||
ctx.load_builtin(lua, name)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,10 @@ use std::{collections::HashMap, env, path::PathBuf, sync::Arc};
|
|||
use mlua::prelude::*;
|
||||
use tokio::{fs, sync::Mutex as AsyncMutex};
|
||||
|
||||
use crate::lune::scheduler::{IntoLuaOwnedThread, Scheduler, SchedulerThreadId};
|
||||
use crate::lune::{
|
||||
builtins::LuneBuiltin,
|
||||
scheduler::{IntoLuaOwnedThread, Scheduler, SchedulerThreadId},
|
||||
};
|
||||
|
||||
const REGISTRY_KEY: &str = "RequireContext";
|
||||
|
||||
|
@ -11,6 +14,7 @@ const REGISTRY_KEY: &str = "RequireContext";
|
|||
pub(super) struct RequireContext {
|
||||
use_absolute_paths: bool,
|
||||
working_directory: PathBuf,
|
||||
cache_builtins: Arc<AsyncMutex<HashMap<LuneBuiltin, LuaResult<LuaRegistryKey>>>>,
|
||||
cache_results: Arc<AsyncMutex<HashMap<PathBuf, LuaResult<LuaRegistryKey>>>>,
|
||||
cache_pending: Arc<AsyncMutex<HashMap<PathBuf, SchedulerThreadId>>>,
|
||||
}
|
||||
|
@ -24,11 +28,13 @@ impl RequireContext {
|
|||
than one context may lead to undefined require-behavior.
|
||||
*/
|
||||
pub fn new() -> Self {
|
||||
let cwd = env::current_dir().expect("Failed to get current working directory");
|
||||
Self {
|
||||
// TODO: Set to false by default, load some kind of config
|
||||
// or env var to check if we should be using absolute paths
|
||||
use_absolute_paths: true,
|
||||
working_directory: env::current_dir().expect("Failed to get current working directory"),
|
||||
working_directory: cwd,
|
||||
cache_builtins: Arc::new(AsyncMutex::new(HashMap::new())),
|
||||
cache_results: Arc::new(AsyncMutex::new(HashMap::new())),
|
||||
cache_pending: Arc::new(AsyncMutex::new(HashMap::new())),
|
||||
}
|
||||
|
@ -224,6 +230,58 @@ impl RequireContext {
|
|||
|
||||
thread_res
|
||||
}
|
||||
|
||||
/**
|
||||
Loads (requires) the builtin with the given name.
|
||||
*/
|
||||
pub fn load_builtin<'lua>(
|
||||
&self,
|
||||
lua: &'lua Lua,
|
||||
name: impl AsRef<str>,
|
||||
) -> LuaResult<LuaMultiValue<'lua>>
|
||||
where
|
||||
'lua: 'static, // FIXME: Remove static lifetime bound here when builtin libraries no longer need it
|
||||
{
|
||||
let builtin: LuneBuiltin = match name.as_ref().parse() {
|
||||
Err(e) => return Err(LuaError::runtime(e)),
|
||||
Ok(b) => b,
|
||||
};
|
||||
|
||||
let mut cache = self
|
||||
.cache_builtins
|
||||
.try_lock()
|
||||
.expect("RequireContext may not be used from multiple threads");
|
||||
|
||||
if let Some(res) = cache.get(&builtin) {
|
||||
return match res {
|
||||
Err(e) => return Err(e.clone()),
|
||||
Ok(key) => {
|
||||
let multi_vec = lua
|
||||
.registry_value::<Vec<LuaValue>>(key)
|
||||
.expect("Missing builtin result in lua registry");
|
||||
Ok(LuaMultiValue::from_vec(multi_vec))
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
let result = builtin.create(lua);
|
||||
|
||||
cache.insert(
|
||||
builtin,
|
||||
match result.clone() {
|
||||
Err(e) => Err(e),
|
||||
Ok(multi) => {
|
||||
let multi_vec = multi.into_vec();
|
||||
let multi_key = lua
|
||||
.create_registry_value(multi_vec)
|
||||
.expect("Failed to store require result in registry");
|
||||
Ok(multi_key)
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for RequireContext {}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::process::ExitCode;
|
||||
|
||||
mod builtins;
|
||||
mod error;
|
||||
mod globals;
|
||||
mod scheduler;
|
||||
|
|
|
@ -52,7 +52,8 @@ impl<'lua> TableBuilder<'lua> {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: Remove static lifetime bound here when possible and move into above impl
|
||||
// FIXME: Remove static lifetime bound here when `create_async_function`
|
||||
// no longer needs it to compile, then move this into the above impl
|
||||
impl<'lua> TableBuilder<'lua>
|
||||
where
|
||||
'lua: 'static,
|
||||
|
|
Loading…
Reference in a new issue