|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- package fs
-
- import (
- "os"
- "path/filepath"
- "regexp"
- "sync"
- "time"
-
- log "github.com/sirupsen/logrus"
- "github.com/fsnotify/fsnotify"
- )
-
- type watcher struct {
- dir string
- mutex sync.Mutex
-
- daemon *fsnotify.Watcher
- event chan *fsnotify.Event
-
- ignores []string
- watchList map[*regexp.Regexp]func(string)
- }
-
- func NewWatcher(dir string) *watcher {
- self := new(watcher)
- self.dir = dir
- self.event = make(chan *fsnotify.Event)
- self.ignores = []string{}
- self.watchList = make(map[*regexp.Regexp]func(string))
- return self
- }
-
- // IsWrite checks if the triggered event is fsnotify.Write|fsnotify.Create.
- func (self *watcher) isWrite(event *fsnotify.Event) bool {
- // instead of MODIFY event, editors may only send CREATE.
- // so we need to capture write & create.
- if event.Op&fsnotify.Write == fsnotify.Write ||
- event.Op&fsnotify.Create == fsnotify.Create {
- return true
- }
- return false
- }
-
- // IsRemove checks if the triggered event is fsnotify.Remove.
- func (self *watcher) isRemove(event *fsnotify.Event) bool {
- return event.Op&fsnotify.Remove == fsnotify.Remove
- }
-
- // Add appends regular expression based pattern processor into the watch list.
- func (self *watcher) Add(pattern *regexp.Regexp, process func(path string)) {
- self.mutex.Lock()
- defer self.mutex.Unlock()
- self.watchList[pattern] = process
- }
-
- func (self *watcher) watch() {
- self.daemon, _ = fsnotify.NewWatcher()
- if self.daemon != nil {
- self.daemon.Close()
- }
- // ensure we have a new daemon watcher eachtime we start watching.
- self.daemon, _ = fsnotify.NewWatcher()
- if err := self.daemon.Add(self.dir); err != nil {
- log.Fatalf("Failed to create fs watcher for <%s>: %v", self.dir, err)
- }
-
- // watch all folders under the root.
- filepath.Walk(self.dir, func(path string, info os.FileInfo, e error) error {
- if info.IsDir() {
- for _, ignore := range self.ignores {
- if info.Name() == ignore {
- return filepath.SkipDir
- }
- }
- if err := self.daemon.Add(path); err != nil {
- log.Fatalf("Failed create watch list for (%s): %v", info.Name(), err)
- }
- }
- return e
- })
- }
-
- func (self *watcher) startWatching() {
- self.watch()
-
- var evt *fsnotify.Event
- // multiple events can be triggered on a successful write
- // (e.g. Create followed by multiple CHMOD), just postpone
- // a bit to let it calm before actual processing.
- var delay <-chan time.Time
- for {
- select {
- case event := <-self.daemon.Events:
- // We only need "Write" event (modify | create | remove)
- if self.isWrite(&event) || self.isRemove(&event) {
- evt = &event
- delay = time.After(500 * time.Millisecond)
- }
- case err := <-self.daemon.Errors:
- log.Fatalf("Failed to watch the path %v", err)
-
- case <-delay:
- self.event <- evt
- }
- }
- }
-
- // Start watches all file changes under the root path & dispatch
- // to corresonding handlers (added via Add function)
- func (self *watcher) Start() {
- go self.startWatching()
- // listens the catched event & start processing.
- for event := range self.event {
- if event == nil {
- continue
- }
- // start processing the event
- var filename = filepath.Base(event.Name)
- for pattern, process := range self.watchList {
- if pattern.MatchString(filename) {
- process(event.Name)
- }
- }
- }
- }
|