package modules import ( "embed" "fmt" "maps" "Moonshark/modules/crypto" "Moonshark/modules/fs" "Moonshark/modules/http" "Moonshark/modules/json" "Moonshark/modules/math" lua_string "Moonshark/modules/string" luajit "git.sharkk.net/Sky/LuaJIT-to-Go" ) // Global registry instance var Global *Registry //go:embed crypto/*.lua fs/*.lua json/*.lua math/*.lua string/*.lua http/*.lua var embeddedModules embed.FS // Registry manages all Lua modules and Go functions type Registry struct { modules map[string]string goFuncs map[string]luajit.GoFunction } // New creates a new registry with all modules loaded func New() *Registry { r := &Registry{ modules: make(map[string]string), goFuncs: make(map[string]luajit.GoFunction), } // Load all Go functions maps.Copy(r.goFuncs, json.GetFunctionList()) maps.Copy(r.goFuncs, lua_string.GetFunctionList()) maps.Copy(r.goFuncs, math.GetFunctionList()) maps.Copy(r.goFuncs, fs.GetFunctionList()) maps.Copy(r.goFuncs, crypto.GetFunctionList()) maps.Copy(r.goFuncs, http.GetFunctionList()) r.loadEmbeddedModules() return r } // loadEmbeddedModules discovers and loads all .lua files func (r *Registry) loadEmbeddedModules() { // Discover all directories from embed dirs, _ := embeddedModules.ReadDir(".") for _, dir := range dirs { if !dir.IsDir() { continue } // Assume one module file per directory: dirname/dirname.lua modulePath := fmt.Sprintf("%s/%s.lua", dir.Name(), dir.Name()) if source, err := embeddedModules.ReadFile(modulePath); err == nil { r.modules[dir.Name()] = string(source) } } } // InstallInState sets up the complete module system in a Lua state func (r *Registry) InstallInState(state *luajit.State) error { // Create moonshark global table with Go functions state.NewTable() for name, fn := range r.goFuncs { if err := state.PushGoFunction(fn); err != nil { return fmt.Errorf("failed to register Go function '%s': %w", name, err) } state.SetField(-2, name) } state.SetGlobal("moonshark") // Backup original require and install custom one state.GetGlobal("require") state.SetGlobal("_require_original") return state.RegisterGoFunction("require", func(s *luajit.State) int { if err := s.CheckMinArgs(1); err != nil { return s.PushError("require: %v", err) } moduleName, err := s.SafeToString(1) if err != nil { return s.PushError("require: module name must be a string") } // Check built-in modules first if source, exists := r.modules[moduleName]; exists { if err := s.LoadString(source); err != nil { return s.PushError("require: failed to load module '%s': %v", moduleName, err) } if err := s.Call(0, 1); err != nil { return s.PushError("require: failed to execute module '%s': %v", moduleName, err) } return 1 } // Fall back to original require s.GetGlobal("_require_original") if s.IsFunction(-1) { s.PushString(moduleName) if err := s.Call(1, 1); err != nil { return s.PushError("require: %v", err) } return 1 } return s.PushError("require: module '%s' not found", moduleName) }) } // Initialize sets up the global registry func Initialize() error { Global = New() return nil }