#[cfg(feature = "sandbox")]
use luars::SandboxConfig;
use luars::lua_vm::SafeOption;
use luars::lua_vm::{LuaTypedAsyncCallback, LuaTypedCallback};
use luars::{
FromLua, FromLuaMulti, IntoLua, LuaEnum, LuaLibrary, LuaRegistrable, LuaResult, LuaUserdata,
LuaVM, Stdlib, UserDataRef, UserDataTrait,
};
use crate::lua_api::util::{collect_values, from_value, into_single_value};
use crate::lua_api::{Chunk, Function, LuaString, Scope, Table, Value};
pub struct Lua {
vm: Box<LuaVM>,
}
impl Lua {
pub fn new(option: SafeOption) -> Self {
Lua {
vm: LuaVM::new(option),
}
}
#[inline]
pub fn open_stdlib(&mut self, lib: Stdlib) -> LuaResult<()> {
self.vm.open_stdlib(lib)
}
#[inline]
pub fn install_library<L: LuaLibrary>(&mut self, library: L) -> LuaResult<()> {
library.install_lua(self)
}
#[inline]
pub fn load_stdlibs(&mut self, lib: Stdlib) -> LuaResult<()> {
self.open_stdlib(lib)
}
#[inline]
pub fn collect_garbage(&mut self) -> LuaResult<()> {
self.vm.main_state().collect_garbage()
}
#[inline]
pub fn execute(&mut self, source: &str) -> LuaResult<()> {
self.vm.execute(source).map(|_| ())
}
pub(crate) fn load_value(&mut self, source: &str) -> LuaResult<luars::LuaValue> {
self.vm.load(source)
}
pub(crate) fn load_value_with_name(
&mut self,
source: &str,
chunk_name: &str,
) -> LuaResult<luars::LuaValue> {
self.vm.load_with_name(source, chunk_name)
}
pub(crate) fn value_to_function(&mut self, value: luars::LuaValue) -> LuaResult<Function> {
let function = self
.vm
.to_function_ref(value)
.ok_or_else(|| self.vm.error("compiled chunk is not a function"))?;
Ok(Function::new(function))
}
pub(crate) fn value_to_string(&mut self, value: luars::LuaValue) -> LuaResult<LuaString> {
let string = self
.vm
.to_string_ref(value)
.ok_or_else(|| self.vm.error("value is not a string"))?;
Ok(LuaString::new(string))
}
pub(crate) fn value_to_userdata<T: 'static>(
&mut self,
value: luars::LuaValue,
) -> LuaResult<UserDataRef<T>> {
self.vm
.to_userdata_ref(value)
.ok_or_else(|| self.vm.error("value is not the expected userdata type"))
}
pub(crate) fn call_function_value(
&mut self,
func: luars::LuaValue,
) -> LuaResult<Vec<luars::LuaValue>> {
self.vm.call_raw(func, vec![])
}
pub(crate) async fn call_function_value_async(
&mut self,
func: luars::LuaValue,
args: Vec<luars::LuaValue>,
) -> LuaResult<Vec<luars::LuaValue>> {
self.vm.call_async(func, args).await
}
pub(crate) fn pack_multi<T: IntoLua>(
&mut self,
value: T,
_api_name: &str,
) -> LuaResult<Vec<luars::LuaValue>> {
collect_values(&mut self.vm, value)
}
#[cfg(feature = "sandbox")]
pub(crate) fn load_sandboxed_value(
&mut self,
source: &str,
config: &SandboxConfig,
) -> LuaResult<luars::LuaValue> {
self.vm.load_sandboxed(source, config)
}
#[cfg(feature = "sandbox")]
pub(crate) fn load_sandboxed_value_with_name(
&mut self,
source: &str,
chunk_name: &str,
config: &SandboxConfig,
) -> LuaResult<luars::LuaValue> {
self.vm.load_with_name_sandboxed(source, chunk_name, config)
}
pub(crate) fn unpack_multi_values<R: FromLuaMulti>(
&mut self,
values: Vec<luars::LuaValue>,
api_name: &str,
) -> LuaResult<R> {
R::from_lua_multi(values, self.vm.main_state())
.map_err(|msg| self.vm.error(format!("{}: {}", api_name, msg)))
}
pub(crate) fn unpack_value<T: FromLua>(
&mut self,
value: luars::LuaValue,
api_name: &str,
) -> LuaResult<T> {
from_value(&mut self.vm, value, api_name)
}
pub fn eval<R: FromLua>(&mut self, source: &str) -> LuaResult<R> {
let values = self.vm.execute(source)?;
let value = values
.into_iter()
.next()
.unwrap_or_else(luars::LuaValue::nil);
from_value(&mut self.vm, value, "eval")
}
pub fn eval_multi<R: FromLuaMulti>(&mut self, source: &str) -> LuaResult<R> {
let values = self.vm.execute(source)?;
R::from_lua_multi(values, self.vm.main_state()).map_err(|msg| self.vm.error(msg))
}
pub async fn exec_async(&mut self, source: &str) -> LuaResult<()> {
self.load(source).exec_async().await
}
pub async fn eval_async<R: FromLua>(&mut self, source: &str) -> LuaResult<R> {
self.load(source).eval_async().await
}
pub async fn eval_multi_async<R: FromLuaMulti>(&mut self, source: &str) -> LuaResult<R> {
self.load(source).eval_multi_async().await
}
pub fn set_global<T: IntoLua>(&mut self, name: &str, value: T) -> LuaResult<()> {
let value = into_single_value(&mut self.vm, value, "set_global")?;
self.vm.set_global(name, value)
}
#[inline]
pub fn globals(&mut self) -> Table {
Table::new(self.vm.globals_table())
}
#[inline]
pub fn get_global<T: FromLua>(&mut self, name: &str) -> LuaResult<Option<T>> {
self.vm.get_global_as(name)
}
#[inline]
pub fn call_global<A: IntoLua, R: FromLuaMulti>(
&mut self,
name: &str,
args: A,
) -> LuaResult<R> {
self.vm.call_global(name, args)
}
#[inline]
pub fn call_global1<A: IntoLua, R: FromLua>(&mut self, name: &str, args: A) -> LuaResult<R> {
self.vm.call1_global(name, args)
}
pub async fn call_async<A: IntoLua, R: FromLuaMulti>(
&mut self,
function: &Function,
args: A,
) -> LuaResult<R> {
let args = self.pack_multi(args, "call_async")?;
let values = self
.call_function_value_async(function.inner.to_value(), args)
.await?;
self.unpack_multi_values(values, "call_async")
}
pub async fn call_async1<A: IntoLua, R: FromLua>(
&mut self,
function: &Function,
args: A,
) -> LuaResult<R> {
let args = self.pack_multi(args, "call_async1")?;
let value = self
.call_function_value_async(function.inner.to_value(), args)
.await?
.into_iter()
.next()
.unwrap_or_else(luars::LuaValue::nil);
self.unpack_value(value, "call_async1")
}
pub async fn call_async_global<A: IntoLua, R: FromLuaMulti>(
&mut self,
name: &str,
args: A,
) -> LuaResult<R> {
let function = self
.get_function(name)?
.ok_or_else(|| self.vm.error(format!("global '{}' not found", name)))?;
self.call_async(&function, args).await
}
pub async fn call_async_global1<A: IntoLua, R: FromLua>(
&mut self,
name: &str,
args: A,
) -> LuaResult<R> {
let function = self
.get_function(name)?
.ok_or_else(|| self.vm.error(format!("global '{}' not found", name)))?;
self.call_async1(&function, args).await
}
#[inline]
pub fn register_function<F, Args, R>(&mut self, name: &str, f: F) -> LuaResult<()>
where
F: LuaTypedCallback<Args, R>,
{
self.vm.register_function_typed(name, f)
}
#[inline]
pub fn create_function<F, Args, R>(&mut self, f: F) -> LuaResult<Function>
where
F: LuaTypedCallback<Args, R>,
{
self.vm.create_function_typed(f).map(Function::new)
}
#[inline]
pub fn register_async_function<F, Args, R>(&mut self, name: &str, f: F) -> LuaResult<()>
where
F: LuaTypedAsyncCallback<Args, R>,
{
self.vm.register_async_typed(name, f)
}
#[inline]
pub fn register_type_of<T: LuaRegistrable>(&mut self, name: &str) -> LuaResult<()> {
self.vm.register_type_of::<T>(name)
}
pub fn register_type<T: LuaRegistrable>(&mut self, name: &str) -> LuaResult<Table> {
self.vm.register_type_of::<T>(name)?;
self.get_table(name)?.ok_or_else(|| {
self.vm.error(format!(
"registered type '{}' did not produce a table",
name
))
})
}
#[inline]
pub fn register_enum_of<T: LuaEnum>(&mut self, name: &str) -> LuaResult<()> {
self.vm.register_enum_of::<T>(name)
}
pub fn load<'lua>(&'lua mut self, source: &str) -> Chunk<'lua> {
Chunk::new(self, source)
}
#[cfg(feature = "sandbox")]
pub fn load_sandboxed<'lua>(
&'lua mut self,
source: &str,
config: &SandboxConfig,
) -> Chunk<'lua> {
self.load(source).with_sandbox(config)
}
#[cfg(feature = "sandbox")]
pub fn execute_sandboxed(&mut self, source: &str, config: &SandboxConfig) -> LuaResult<()> {
self.vm.execute_sandboxed(source, config).map(|_| ())
}
#[cfg(feature = "sandbox")]
pub fn eval_sandboxed<R: FromLua>(
&mut self,
source: &str,
config: &SandboxConfig,
) -> LuaResult<R> {
let value = self
.vm
.execute_sandboxed(source, config)?
.into_iter()
.next()
.unwrap_or_else(luars::LuaValue::nil);
self.unpack_value(value, "eval_sandboxed")
}
#[cfg(feature = "sandbox")]
pub fn eval_multi_sandboxed<R: FromLuaMulti>(
&mut self,
source: &str,
config: &SandboxConfig,
) -> LuaResult<R> {
let values = self.vm.execute_sandboxed(source, config)?;
self.unpack_multi_values(values, "eval_multi_sandboxed")
}
#[cfg(feature = "sandbox")]
pub fn sandbox_capture_global(
&mut self,
config: &mut SandboxConfig,
name: &str,
) -> LuaResult<()> {
let value = self
.vm
.get_global(name)?
.ok_or_else(|| self.vm.error(format!("global '{}' not found", name)))?;
config.insert_global(name, value);
Ok(())
}
#[cfg(feature = "sandbox")]
pub fn sandbox_insert_global<T: IntoLua>(
&mut self,
config: &mut SandboxConfig,
name: &str,
value: T,
) -> LuaResult<()> {
let value = into_single_value(&mut self.vm, value, "sandbox_insert_global")?;
config.insert_global(name, value);
Ok(())
}
pub fn scope<'lua, R>(
&'lua mut self,
f: impl for<'scope> FnOnce(&mut Scope<'scope, 'lua>) -> LuaResult<R>,
) -> LuaResult<R> {
let mut scope = Scope::new(self);
f(&mut scope)
}
pub fn load_function(&mut self, source: &str) -> LuaResult<Function> {
self.load(source).into_function()
}
pub fn create_string(&mut self, value: &str) -> LuaResult<LuaString> {
let value = self.vm.create_string(value)?;
self.value_to_string(value)
}
pub(crate) fn create_raw_function<F>(&mut self, f: F) -> LuaResult<Function>
where
F: Fn(&mut luars::LuaState) -> LuaResult<usize> + 'static,
{
let value = self.vm.create_closure(f)?;
self.value_to_function(value)
}
pub(crate) fn create_userdata_value<T: UserDataTrait + 'static>(
&mut self,
data: T,
) -> LuaResult<Value> {
let value = self.vm.create_userdata(LuaUserdata::new(data))?;
Ok(Value::new(self.vm.to_ref(value)))
}
pub fn create_table(&mut self) -> LuaResult<Table> {
self.create_table_with_capacity(0, 0)
}
pub fn create_table_with_capacity(&mut self, narr: usize, nrec: usize) -> LuaResult<Table> {
self.vm.create_table_ref(narr, nrec).map(Table::new)
}
pub fn create_userdata<T: UserDataTrait + 'static>(
&mut self,
data: T,
) -> LuaResult<UserDataRef<T>> {
let value = self.vm.create_userdata(LuaUserdata::new(data))?;
self.value_to_userdata(value)
}
pub unsafe fn create_userdata_ref<T: UserDataTrait + 'static>(
&mut self,
reference: &mut T,
) -> LuaResult<UserDataRef<T>> {
let value = unsafe { self.vm.main_state().create_userdata_ref(reference) }?;
self.value_to_userdata(value)
}
pub fn create_table_from<K, V, I>(&mut self, iter: I) -> LuaResult<Table>
where
K: IntoLua,
V: IntoLua,
I: IntoIterator<Item = (K, V)>,
{
let table = self.create_table()?;
for (key, value) in iter {
table.set(key, value)?;
}
Ok(table)
}
pub fn create_sequence_from<T, I>(&mut self, iter: I) -> LuaResult<Table>
where
T: IntoLua,
I: IntoIterator<Item = T>,
{
let table = self.create_table()?;
for value in iter {
table.push(value)?;
}
Ok(table)
}
pub fn get_function(&mut self, name: &str) -> LuaResult<Option<Function>> {
self.vm
.get_global_function(name)
.map(|opt| opt.map(Function::new))
}
pub fn get_table(&mut self, name: &str) -> LuaResult<Option<Table>> {
self.vm
.get_global_table(name)
.map(|opt| opt.map(Table::new))
}
pub fn set_global_table(&mut self, name: &str, table: &Table) -> LuaResult<()> {
self.vm.set_global(name, table.value())
}
pub fn set_global_function(&mut self, name: &str, function: &Function) -> LuaResult<()> {
self.vm.set_global(name, function.inner.to_value())
}
pub fn table_set<T: IntoLua>(&mut self, table: &Table, key: &str, value: T) -> LuaResult<()> {
let value = into_single_value(&mut self.vm, value, "table_set")?;
table.inner.set(key, value)
}
pub fn table_seti<T: IntoLua>(&mut self, table: &Table, key: i64, value: T) -> LuaResult<()> {
let value = into_single_value(&mut self.vm, value, "table_seti")?;
table.inner.seti(key, value)
}
pub fn table_get<T: FromLua>(&mut self, table: &Table, key: &str) -> LuaResult<T> {
let value = table.inner.get(key)?;
from_value(&mut self.vm, value, "table_get")
}
pub fn table_geti<T: FromLua>(&mut self, table: &Table, key: i64) -> LuaResult<T> {
let value = table.inner.geti(key)?;
from_value(&mut self.vm, value, "table_geti")
}
pub fn table_push<T: IntoLua>(&mut self, table: &Table, value: T) -> LuaResult<()> {
let value = into_single_value(&mut self.vm, value, "table_push")?;
table.inner.push(value)
}
pub fn table_pairs<K: FromLua, V: FromLua>(&mut self, table: &Table) -> LuaResult<Vec<(K, V)>> {
table.pairs()
}
pub fn table_array<T: FromLua>(&mut self, table: &Table) -> LuaResult<Vec<T>> {
table.sequence_values()
}
#[inline]
pub fn pack<T: IntoLua>(&mut self, value: T) -> LuaResult<Value> {
let value = into_single_value(&mut self.vm, value, "pack")?;
Ok(Value::new(self.vm.to_ref(value)))
}
#[inline]
pub fn unpack<T: FromLua>(&mut self, value: Value) -> LuaResult<T> {
self.unpack_value(value.to_value(), "unpack")
}
#[inline]
pub fn convert<T: IntoLua, U: FromLua>(&mut self, value: T) -> LuaResult<U> {
let value = into_single_value(&mut self.vm, value, "convert")?;
self.unpack_value(value, "convert")
}
pub unsafe fn vm_mut(&mut self) -> &mut LuaVM {
&mut self.vm
}
}
impl Default for Lua {
fn default() -> Self {
Self::new(SafeOption::default())
}
}