piframe-go/vendor/github.com/dietsche/rfsnotify/rfsnotify.go

134 lines
3.1 KiB
Go

// Package rfsnotify implements recursive folder monitoring by wrapping the excellent fsnotify library
package rfsnotify
import (
"gopkg.in/fsnotify.v1"
"errors"
"os"
"path/filepath"
)
// RWatcher wraps fsnotify.Watcher. When fsnotify adds recursive watches, you should be able to switch your code to use fsnotify.Watcher
type RWatcher struct {
Events chan fsnotify.Event
Errors chan error
done chan struct{}
fsnotify *fsnotify.Watcher
isClosed bool
}
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
func NewWatcher() (*RWatcher, error) {
fsWatch, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
m := &RWatcher{}
m.fsnotify = fsWatch
m.Events = make(chan fsnotify.Event)
m.Errors = make(chan error)
m.done = make(chan struct{})
go m.start()
return m, nil
}
// Add starts watching the named file or directory (non-recursively).
func (m *RWatcher) Add(name string) error {
if m.isClosed {
return errors.New("rfsnotify instance already closed")
}
return m.fsnotify.Add(name)
}
// AddRecursive starts watching the named directory and all sub-directories.
func (m *RWatcher) AddRecursive(name string) error {
if m.isClosed {
return errors.New("rfsnotify instance already closed")
}
if err := m.watchRecursive(name, false); err != nil {
return err
}
return nil
}
// Remove stops watching the the named file or directory (non-recursively).
func (m *RWatcher) Remove(name string) error {
return m.fsnotify.Remove(name)
}
// RemoveRecursive stops watching the named directory and all sub-directories.
func (m *RWatcher) RemoveRecursive(name string) error {
if err := m.watchRecursive(name, true); err != nil {
return err
}
return nil
}
// Close removes all watches and closes the events channel.
func (m *RWatcher) Close() error {
if m.isClosed {
return nil
}
close(m.done)
m.isClosed = true
return nil
}
func (m *RWatcher) start() {
for {
select {
case e := <-m.fsnotify.Events:
s, err := os.Stat(e.Name)
if err == nil && s != nil && s.IsDir() {
if e.Op&fsnotify.Create != 0 {
m.watchRecursive(e.Name, false)
}
}
//Can't stat a deleted directory, so just pretend that it's always a directory and
//try to remove from the watch list... we really have no clue if it's a directory or not...
if e.Op&fsnotify.Remove != 0 {
m.fsnotify.Remove(e.Name)
}
m.Events <- e
case e := <-m.fsnotify.Errors:
m.Errors <- e
case <-m.done:
m.fsnotify.Close()
close(m.Events)
close(m.Errors)
return
}
}
}
// watchRecursive adds all directories under the given one to the watch list.
// this is probably a very racey process. What if a file is added to a folder before we get the watch added?
func (m *RWatcher) watchRecursive(path string, unWatch bool) error {
err := filepath.Walk(path, func(walkPath string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
if fi.IsDir() {
if unWatch {
if err = m.fsnotify.Remove(walkPath); err != nil {
return err
}
} else {
if err = m.fsnotify.Add(walkPath); err != nil {
return err
}
}
}
return nil
})
return err
}