package storage import ( "bufio" "log" "my/ktgo/pkg/lib" "os" "strings" "sync" "time" "github.com/fsnotify/fsnotify" ) type Storage struct { filename string fd *os.File watcher *fsnotify.Watcher list List lock sync.RWMutex } type List []string func NewStorage(filename string) (*Storage, error) { s := &Storage{ filename: filename, } err := s.reCreateWatcher() if err != nil { return nil, err } return s, nil } func (s *Storage) reReadFile() error { fd, err := os.OpenFile(s.filename, os.O_RDONLY, 0) if err != nil { return err } var newlist List scanner := bufio.NewScanner(fd) for { ok := scanner.Scan() if !ok { break } line := scanner.Text() newlist = append(newlist, line) } s.lock.Lock() defer s.lock.Unlock() s.list = newlist return nil } func (s *Storage) reCreateWatcher() error { go s.reCreateWatcher_sync() return nil } func (s *Storage) reCreateWatcher_sync() error { s.closeWatcher() s.waitForAndReadFile() watcher, err := s.createNewWatcher() if err != nil { return err } s.lock.Lock() defer s.lock.Unlock() s.watcher = watcher go s.watcherListener() return nil } func (s *Storage) waitForAndReadFile() { for { err := s.reReadFile() if err == nil { break } time.Sleep(100 * time.Millisecond) } } func (s *Storage) createNewWatcher() (*fsnotify.Watcher, error) { watcher, err := fsnotify.NewWatcher() if err != nil { return nil, err } err = watcher.Add(s.filename) if err != nil { return nil, err } return watcher, nil } func (s *Storage) watcherListener() { for { select { case event, ok := <-s.watcher.Events: if !ok { return } s.handleWatcherEvent(event) case err, ok := <-s.watcher.Errors: if !ok { return } log.Println("error:", err) } } } func (s *Storage) handleWatcherEvent(event fsnotify.Event) { log.Printf("Storage: %v: %v", s.filename, event.String()) s.onStorageUpdated() } func (s *Storage) onStorageUpdated() { err := s.reCreateWatcher() if err != nil { log.Printf("Storage watcher creating: %v", err) } } func (s *Storage) GetList() *List { s.lock.RLock() defer s.lock.RUnlock() return &s.list } func (s *Storage) AddLine(p, v string) error { return nil } func (s *Storage) Save() error { s.makeBackup() return s.doSave() } func (s *Storage) doSave() error { buf := strings.Join(s.list, "\n") return lib.WriteFile(s.filename, buf) } func (s *Storage) makeBackup() { cmd := "cp " + s.filename + " " + s.filename + ".backup" lib.Bash(cmd) } func (s *Storage) Close() { s.closeWatcher() } func (s *Storage) closeWatcher() { if s.watcher != nil { s.watcher.Close() s.watcher = nil } }