2018-06-22 07:01:36 -05:00
|
|
|
package gaper
|
2018-06-16 19:22:21 -05:00
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2018-06-18 21:47:20 -05:00
|
|
|
"fmt"
|
2018-06-16 19:22:21 -05:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"time"
|
2018-06-18 21:47:20 -05:00
|
|
|
|
|
|
|
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 {
|
2018-06-18 21:21:43 -05:00
|
|
|
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
|
2018-06-18 21:47:20 -05:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2018-06-18 21:47:20 -05:00
|
|
|
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),
|
2018-06-18 21:21:43 -05:00
|
|
|
PollInterval: pollInterval,
|
2018-06-18 21:47:20 -05:00
|
|
|
WatchItems: watchMatches,
|
|
|
|
IgnoreItems: ignoreMatches,
|
2018-06-16 19:22:21 -05:00
|
|
|
AllowedExtensions: allowedExts,
|
2018-06-18 21:47:20 -05:00
|
|
|
}, 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
|
2018-06-18 21:47:20 -05:00
|
|
|
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
|
|
|
|
2018-06-18 21:21:43 -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
|
|
|
}
|
2018-06-18 21:47:20 -05:00
|
|
|
|
|
|
|
func resolveGlobMatches(paths []string) ([]string, error) {
|
|
|
|
var result []string
|
|
|
|
|
|
|
|
for _, path := range paths {
|
|
|
|
matches, err := zglob.Glob(path)
|
|
|
|
if err != nil {
|
2018-06-22 07:01:36 -05:00
|
|
|
return nil, fmt.Errorf("couldn't resolve glob path \"%s\": %v", path, err)
|
2018-06-18 21:47:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
logger.Debugf("Resolved glob path %s: %v", path, matches)
|
|
|
|
result = append(result, matches...)
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|