use crate::lua_api;
use crate::lua_value::LuaValue;
use crate::lua_vm::LuaState;
use crate::lua_vm::{CFunction, GlobalState, LuaResult};
use crate::stdlib::{self, Stdlib};
pub trait LuaLibrary {
fn install(&self, lua: &mut lua_api::Lua) -> LuaResult<()>;
}
pub type ValueInitializer = fn(&mut GlobalState) -> LuaResult<LuaValue>;
pub type ModuleInitializer = fn(&mut LuaState) -> LuaResult<()>;
pub enum LibraryEntry {
Function(CFunction),
Value(ValueInitializer),
}
pub struct PreloadModule {
pub name: String,
pub loader: CFunction,
}
impl PreloadModule {
pub fn new(name: impl Into<String>, loader: CFunction) -> Self {
Self {
name: name.into(),
loader,
}
}
}
impl LuaLibrary for PreloadModule {
fn install(&self, lua: &mut lua_api::Lua) -> LuaResult<()> {
let vm = lua.global_state_mut();
vm.register_preload(&self.name, self.loader)
}
}
pub struct LibraryModule {
pub name: String,
pub entries: Vec<(&'static str, LibraryEntry)>,
pub initializer: Option<ModuleInitializer>,
}
impl LibraryModule {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
entries: Vec::new(),
initializer: None,
}
}
pub fn with_function(mut self, name: &'static str, func: CFunction) -> Self {
self.entries.push((name, LibraryEntry::Function(func)));
self
}
pub fn with_value(mut self, name: &'static str, value_init: ValueInitializer) -> Self {
self.entries.push((name, LibraryEntry::Value(value_init)));
self
}
pub fn with_initializer(mut self, init: ModuleInitializer) -> Self {
self.initializer = Some(init);
self
}
}
impl LuaLibrary for LibraryModule {
fn install(&self, lua: &mut lua_api::Lua) -> LuaResult<()> {
let vm = lua.global_state_mut();
load_library_module(vm, self)
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! lib_module {
($name:expr, {
$($item_name:expr => $item:expr),* $(,)?
}) => {{
let mut module = $crate::lib_registry::LibraryModule::new($name);
$(
module.entries.push(($item_name, $crate::lib_registry::LibraryEntry::Function($item)));
)*
module
}};
}
#[macro_export]
macro_rules! lua_module {
($name:expr, { $($items:tt)* }) => {{
let mut module = $crate::LibraryModule::new($name);
$crate::__lua_module_items!(module, $($items)*);
module
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __lua_module_items {
($module:ident,) => {};
($module:ident) => {};
($module:ident, init => $init:expr $(, $($rest:tt)*)?) => {{
$module = $module.with_initializer($init);
$crate::__lua_module_items!($module $(, $($rest)*)?);
}};
($module:ident, value $name:expr => $value:expr $(, $($rest:tt)*)?) => {{
$module = $module.with_value($name, $value);
$crate::__lua_module_items!($module $(, $($rest)*)?);
}};
($module:ident, $name:expr => $func:expr $(, $($rest:tt)*)?) => {{
$module = $module.with_function($name, $func);
$crate::__lua_module_items!($module $(, $($rest)*)?);
}};
}
#[macro_export]
macro_rules! lua_preload_module {
($name:expr => $loader:expr) => {{ $crate::PreloadModule::new($name, $loader) }};
}
pub struct LibraryRegistry {
modules: Vec<LibraryModule>, }
impl LibraryRegistry {
pub fn new() -> Self {
Self {
modules: Vec::new(),
}
}
pub fn register(&mut self, module: LibraryModule) {
self.modules.push(module);
}
pub fn load_all(&self, vm: &mut GlobalState) -> LuaResult<()> {
for module in &self.modules {
self.load_module(vm, module)?;
}
Ok(())
}
pub fn load_module(&self, vm: &mut GlobalState, module: &LibraryModule) -> LuaResult<()> {
load_library_module(vm, module)
}
pub fn get_module(&self, name: &str) -> Option<&LibraryModule> {
self.modules.iter().find(|m| m.name == name)
}
}
fn load_library_module(vm: &mut GlobalState, module: &LibraryModule) -> LuaResult<()> {
let lib_table = vm.create_table(0, 0)?;
for (name, entry) in &module.entries {
let value = match entry {
LibraryEntry::Function(func) => LuaValue::cfunction(*func),
LibraryEntry::Value(value_init) => value_init(vm)?,
};
let name_key = vm.create_string(name)?;
vm.raw_set(&lib_table, name_key, value);
}
if module.name == "_G" {
for (name, entry) in &module.entries {
let value = match entry {
LibraryEntry::Function(func) => LuaValue::cfunction(*func),
LibraryEntry::Value(value_init) => value_init(vm)?,
};
vm.set_global(name, value)?;
}
let globals = vm.global;
if let Some(package_table) = vm.get_global("package")?
&& package_table.is_table()
{
let loaded_key = vm.create_string("loaded")?;
if let Some(loaded_table) = vm.raw_get(&package_table, &loaded_key)
&& loaded_table.is_table()
{
let mod_key = vm.create_string("_G")?;
vm.raw_set(&loaded_table, mod_key, globals);
}
}
} else {
vm.set_global(&module.name, lib_table)?;
if module.name == "string" {
vm.set_string_metatable(lib_table)?;
}
if let Some(package_table) = vm.get_global("package")?
&& package_table.is_table()
{
let loaded_key = vm.create_string("loaded")?;
if let Some(loaded_table) = vm.raw_get(&package_table, &loaded_key)
&& loaded_table.is_table()
{
let mod_key = vm.create_string(&module.name)?;
vm.raw_set(&loaded_table, mod_key, lib_table);
}
}
}
if let Some(init_fn) = module.initializer {
init_fn(vm.main_state())?;
}
Ok(())
}
impl Default for LibraryRegistry {
fn default() -> Self {
Self::new()
}
}
pub fn create_standard_registry(open_lib: Stdlib) -> LibraryRegistry {
let mut registry = LibraryRegistry::new();
if matches!(open_lib, Stdlib::All | Stdlib::Package) {
registry.register(stdlib::package::create_package_lib());
}
if matches!(open_lib, Stdlib::All | Stdlib::Basic) {
registry.register(stdlib::basic::create_basic_lib());
}
if matches!(open_lib, Stdlib::All | Stdlib::String) {
registry.register(stdlib::string::create_string_lib());
}
if matches!(open_lib, Stdlib::All | Stdlib::Table) {
registry.register(stdlib::table::create_table_lib());
}
if matches!(open_lib, Stdlib::All | Stdlib::Math) {
registry.register(stdlib::math::create_math_lib());
}
if matches!(open_lib, Stdlib::All | Stdlib::Io) {
registry.register(stdlib::io::create_io_lib());
}
if matches!(open_lib, Stdlib::All | Stdlib::Os) {
registry.register(stdlib::os::create_os_lib());
}
if matches!(open_lib, Stdlib::All | Stdlib::Utf8) {
registry.register(stdlib::utf8::create_utf8_lib());
}
if matches!(open_lib, Stdlib::All | Stdlib::Coroutine) {
registry.register(stdlib::coroutine::create_coroutine_lib());
}
if matches!(open_lib, Stdlib::All | Stdlib::Debug) {
registry.register(stdlib::debug::create_debug_lib());
}
registry
}