package watchers import ( "fmt" "sync" "git.sharkk.net/Sky/Moonshark/core/logger" "git.sharkk.net/Sky/Moonshark/core/routers" "git.sharkk.net/Sky/Moonshark/core/runner" ) // Global watcher manager instance var ( globalManager *WatcherManager globalManagerOnce sync.Once ) // GetWatcherManager returns the global watcher manager, creating it if needed func GetWatcherManager(adaptive bool) *WatcherManager { globalManagerOnce.Do(func() { globalManager = NewWatcherManager(adaptive) }) return globalManager } // WatchDirectory creates a new directory watcher and registers it with the manager func WatchDirectory(config DirectoryWatcherConfig, manager *WatcherManager) (*Watcher, error) { dirWatcher, err := NewDirectoryWatcher(config) if err != nil { return nil, fmt.Errorf("failed to create directory watcher: %w", err) } manager.AddWatcher(dirWatcher) // Create a wrapper Watcher that implements the old interface w := &Watcher{ dir: config.Dir, dirWatch: dirWatcher, manager: manager, } logger.Debug("Started watching directory: %s", config.Dir) return w, nil } // Watcher is a compatibility wrapper that maintains the old API type Watcher struct { dir string dirWatch *DirectoryWatcher manager *WatcherManager } // Close unregisters the watcher from the manager func (w *Watcher) Close() error { w.manager.RemoveWatcher(w.dir) return nil } // WatchLuaRouter sets up a watcher for a LuaRouter's routes directory; also updates // the LuaRunner so that the state can be rebuilt func WatchLuaRouter(router *routers.LuaRouter, runner *runner.Runner, routesDir string) (*Watcher, error) { manager := GetWatcherManager(true) runnerRefresh := func() error { logger.Debug("Refreshing LuaRunner state due to file change") runner.NotifyFileChanged("") return nil } combinedCallback := combineCallbacks(router.Refresh, runnerRefresh) config := DirectoryWatcherConfig{ Dir: routesDir, Callback: combinedCallback, Recursive: true, } watcher, err := WatchDirectory(config, manager) if err != nil { return nil, err } logger.Info("Started watching Lua routes directory: %s", routesDir) return watcher, nil } // WatchStaticRouter sets up a watcher for a StaticRouter's root directory func WatchStaticRouter(router *routers.StaticRouter, staticDir string) (*Watcher, error) { manager := GetWatcherManager(true) config := DirectoryWatcherConfig{ Dir: staticDir, Callback: router.Refresh, Recursive: true, } watcher, err := WatchDirectory(config, manager) if err != nil { return nil, err } logger.Info("Started watching static files directory: %s", staticDir) return watcher, nil } // WatchLuaModules sets up watchers for Lua module directories func WatchLuaModules(luaRunner *runner.Runner, libDirs []string) ([]*Watcher, error) { manager := GetWatcherManager(true) watchers := make([]*Watcher, 0, len(libDirs)) for _, dir := range libDirs { // Create a directory-specific callback dirCopy := dir // Capture for closure callback := func() error { logger.Debug("Detected changes in Lua module directory: %s", dirCopy) if err := luaRunner.RefreshStates(); err != nil { logger.Warning("Error reloading modules: %v", err) } return nil } config := DirectoryWatcherConfig{ Dir: dir, Callback: callback, Recursive: true, } watcher, err := WatchDirectory(config, manager) if err != nil { // Clean up already created watchers for _, w := range watchers { w.Close() } return nil, err } watchers = append(watchers, watcher) logger.Info("Started watching Lua modules directory: %s", dir) } return watchers, nil } // ShutdownWatcherManager closes the global watcher manager if it exists func ShutdownWatcherManager() { if globalManager != nil { globalManager.Close() globalManager = nil } } // combineCallbacks creates a single callback function from multiple callbacks func combineCallbacks(callbacks ...func() error) func() error { return func() error { for _, callback := range callbacks { if err := callback(); err != nil { return err } } return nil } }