Files

74 lines
1.7 KiB
Go
Raw Permalink Normal View History

// 热重载驱动:fsnotify 文件监听 + 防抖(热更新.md §4.5)。
package mods
import (
"fmt"
"path/filepath"
"sync"
"time"
"github.com/fsnotify/fsnotify"
)
// Watcher 脚本文件监听器(开发模式)。
type Watcher struct {
w *fsnotify.Watcher
mu sync.Mutex
reload map[string]func() error // 脚本路径 → 重载函数
timers map[string]*time.Timer // 防抖计时器(500ms)
}
// NewWatcher 创建监听器。
func NewWatcher() (*Watcher, error) {
w, err := fsnotify.NewWatcher()
if err != nil {
return nil, fmt.Errorf("mods.NewWatcher: %w", err)
}
return &Watcher{w: w, reload: make(map[string]func() error), timers: make(map[string]*time.Timer)}, nil
}
// WatchFile 监听一个脚本文件:变化后防抖触发 reload(热更新.md §4.5 防抖 500ms)。
func (w *Watcher) WatchFile(path string, reload func() error) error {
if err := w.w.Add(filepath.Dir(path)); err != nil {
return fmt.Errorf("mods.WatchFile %s: %w", path, err)
}
w.mu.Lock()
w.reload[path] = reload
w.mu.Unlock()
return nil
}
// Run 事件循环(goroutine):文件变化 → 防抖 → 重载。
func (w *Watcher) Run() {
for {
select {
case ev, ok := <-w.w.Events:
if !ok {
return
}
if ev.Op&(fsnotify.Write|fsnotify.Create) == 0 {
continue
}
w.mu.Lock()
fn, ok := w.reload[ev.Name]
if !ok {
w.mu.Unlock()
continue
}
if t, exists := w.timers[ev.Name]; exists {
t.Stop()
}
reloadFn := fn
w.timers[ev.Name] = time.AfterFunc(500*time.Millisecond, func() { _ = reloadFn() })
w.mu.Unlock()
case _, ok := <-w.w.Errors:
if !ok {
return
}
}
}
}
// Close 停止监听。
func (w *Watcher) Close() error { return w.w.Close() }