gaper/watcher.go

129 lines
2.6 KiB
Go
Raw Normal View History

package gaper
2018-06-16 19:22:21 -05:00
import (
"errors"
"fmt"
2018-06-16 19:22:21 -05:00
"os"
"path/filepath"
"time"
zglob "github.com/mattn/go-zglob"
2018-06-16 19:22:21 -05:00
)
2018-06-20 21:04:16 -05:00
// Watcher is a interface for the watch process
2018-06-16 19:22:21 -05:00
type Watcher struct {
PollInterval int
2018-06-16 19:22:21 -05:00
WatchItems []string
IgnoreItems []string
AllowedExtensions map[string]bool
Events chan string
Errors chan error
}
2018-06-20 21:04:16 -05:00
// NewWatcher creates a new watcher
func NewWatcher(pollInterval int, watchItems []string, ignoreItems []string, extensions []string) (*Watcher, error) {
2018-06-20 20:40:09 -05:00
if pollInterval == 0 {
pollInterval = DefaultPoolInterval
}
if len(extensions) == 0 {
extensions = DefaultExtensions
}
2018-06-16 19:22:21 -05:00
allowedExts := make(map[string]bool)
for _, ext := range extensions {
allowedExts["."+ext] = true
}
watchMatches, err := resolveGlobMatches(watchItems)
if err != nil {
return nil, err
}
ignoreMatches, err := resolveGlobMatches(ignoreItems)
if err != nil {
return nil, err
}
2018-06-16 19:22:21 -05:00
return &Watcher{
Events: make(chan string),
Errors: make(chan error),
PollInterval: pollInterval,
WatchItems: watchMatches,
IgnoreItems: ignoreMatches,
2018-06-16 19:22:21 -05:00
AllowedExtensions: allowedExts,
}, nil
2018-06-16 19:22:21 -05:00
}
var startTime = time.Now()
var errDetectedChange = errors.New("done")
2018-06-20 21:04:16 -05:00
// Watch starts watching for file changes
func (w *Watcher) Watch() {
2018-06-16 19:22:21 -05:00
for {
2018-06-18 21:22:18 -05:00
for i := range w.WatchItems {
fileChanged, err := w.scanChange(w.WatchItems[i])
if err != nil {
w.Errors <- err
return
2018-06-16 19:22:21 -05:00
}
2018-06-18 21:22:18 -05:00
if fileChanged != "" {
w.Events <- fileChanged
startTime = time.Now()
2018-06-16 19:22:21 -05:00
}
2018-06-18 21:22:18 -05:00
}
2018-06-16 19:22:21 -05:00
time.Sleep(time.Duration(w.PollInterval) * time.Millisecond)
2018-06-18 21:22:18 -05:00
}
}
2018-06-16 19:22:21 -05:00
2018-06-18 21:22:18 -05:00
func (w *Watcher) scanChange(watchPath string) (string, error) {
logger.Debug("Watching ", watchPath)
var fileChanged string
err := filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
2018-06-20 20:40:09 -05:00
// ignore hidden files and directories
if filepath.Base(path)[0] == '.' {
return nil
2018-06-18 21:22:18 -05:00
}
for _, x := range w.IgnoreItems {
if x == path {
return filepath.SkipDir
2018-06-16 19:22:21 -05:00
}
2018-06-18 21:22:18 -05:00
}
2018-06-16 19:22:21 -05:00
2018-06-18 21:22:18 -05:00
ext := filepath.Ext(path)
if _, ok := w.AllowedExtensions[ext]; ok && info.ModTime().After(startTime) {
fileChanged = path
return errDetectedChange
2018-06-16 19:22:21 -05:00
}
2018-06-18 21:22:18 -05:00
return nil
})
if err != nil && err != errDetectedChange {
return "", err
2018-06-16 19:22:21 -05:00
}
2018-06-18 21:22:18 -05:00
return fileChanged, nil
2018-06-16 19:22:21 -05:00
}
func resolveGlobMatches(paths []string) ([]string, error) {
var result []string
for _, path := range paths {
matches, err := zglob.Glob(path)
if err != nil {
return nil, fmt.Errorf("couldn't resolve glob path \"%s\": %v", path, err)
}
logger.Debugf("Resolved glob path %s: %v", path, matches)
result = append(result, matches...)
}
return result, nil
}