use crate::lua_value::LuaValue;
use crate::lua_vm::LuaState;
use crate::lua_vm::{CFunction, LuaResult, LuaVM};
use crate::stdlib::{self, Stdlib};
pub type ValueInitializer = fn(&mut LuaVM) -> LuaResult<LuaValue>;
pub type ModuleInitializer = fn(&mut LuaState) -> LuaResult<()>;
pub enum LibraryEntry {
Function(CFunction),
Value(ValueInitializer),
}
pub struct LibraryModule {
pub name: &'static str,
pub entries: Vec<(&'static str, LibraryEntry)>,
pub initializer: Option<ModuleInitializer>,
}
impl LibraryModule {
pub const fn new(name: &'static str) -> Self {
Self {
name,
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
}
}
#[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
}};
}
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 LuaVM) -> LuaResult<()> {
for module in &self.modules {
self.load_module(vm, module)?;
}
Ok(())
}
pub fn load_module(&self, vm: &mut LuaVM, 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(())
}
pub fn get_module(&self, name: &str) -> Option<&LibraryModule> {
self.modules.iter().find(|m| m.name == name)
}
}
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
}