act/vendor/github.com/andreaskoch/go-fswatch/folder.go
Jesse Newland 3198627879 --watch
2019-02-09 20:39:09 -06:00

250 lines
5.6 KiB
Go

// Copyright 2013 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fswatch
import (
"fmt"
"io/ioutil"
"path/filepath"
"time"
)
var numberOfFolderWatchers int
func init() {
numberOfFolderWatchers = 0
}
func NumberOfFolderWatchers() int {
return numberOfFolderWatchers
}
type FolderWatcher struct {
changeDetails chan *FolderChange
modified chan bool
moved chan bool
stopped chan bool
recurse bool
skipFile func(path string) bool
debug bool
folder string
running bool
wasStopped bool
checkInterval time.Duration
previousEntries []string
}
func NewFolderWatcher(folderPath string, recurse bool, skipFile func(path string) bool, checkIntervalInSeconds int) *FolderWatcher {
if checkIntervalInSeconds < 1 {
panic(fmt.Sprintf("Cannot create a folder watcher with a check interval of %v seconds.", checkIntervalInSeconds))
}
return &FolderWatcher{
modified: make(chan bool),
moved: make(chan bool),
stopped: make(chan bool),
changeDetails: make(chan *FolderChange),
recurse: recurse,
skipFile: skipFile,
debug: true,
folder: folderPath,
checkInterval: time.Duration(checkIntervalInSeconds),
}
}
func (folderWatcher *FolderWatcher) String() string {
return fmt.Sprintf("Folderwatcher %q", folderWatcher.folder)
}
func (folderWatcher *FolderWatcher) Modified() chan bool {
return folderWatcher.modified
}
func (folderWatcher *FolderWatcher) Moved() chan bool {
return folderWatcher.moved
}
func (folderWatcher *FolderWatcher) Stopped() chan bool {
return folderWatcher.stopped
}
func (folderWatcher *FolderWatcher) ChangeDetails() chan *FolderChange {
return folderWatcher.changeDetails
}
func (folderWatcher *FolderWatcher) Start() {
folderWatcher.running = true
sleepInterval := time.Second * folderWatcher.checkInterval
go func() {
// get existing entries
var entryList []string
directory := folderWatcher.folder
previousEntryList := folderWatcher.getPreviousEntryList()
if previousEntryList != nil {
// use the entry list from a previous run
entryList = previousEntryList
} else {
// use a new entry list
newEntryList, _ := getFolderEntries(directory, folderWatcher.recurse, folderWatcher.skipFile)
entryList = newEntryList
}
// increment watcher count
numberOfFolderWatchers++
for folderWatcher.wasStopped == false {
// get new entries
updatedEntryList, _ := getFolderEntries(directory, folderWatcher.recurse, folderWatcher.skipFile)
// check for new items
newItems := make([]string, 0)
modifiedItems := make([]string, 0)
for _, entry := range updatedEntryList {
if isNewItem := !sliceContainsElement(entryList, entry); isNewItem {
// entry is new
newItems = append(newItems, entry)
continue
}
// check if the file changed
if newModTime, err := getLastModTimeFromFile(entry); err == nil {
// check if file has been modified
timeOfLastCheck := time.Now().Add(sleepInterval * -1)
if timeOfLastCheck.Before(newModTime) {
// existing entry has been modified
modifiedItems = append(modifiedItems, entry)
}
}
}
// check for moved items
movedItems := make([]string, 0)
for _, entry := range entryList {
isMoved := !sliceContainsElement(updatedEntryList, entry)
if isMoved {
movedItems = append(movedItems, entry)
}
}
// assign the new list
entryList = updatedEntryList
// sleep
time.Sleep(sleepInterval)
// check if something happened
if len(newItems) > 0 || len(movedItems) > 0 || len(modifiedItems) > 0 {
// send out change
go func() {
folderWatcher.modified <- true
}()
go func() {
log("Folder %q changed", directory)
folderWatcher.changeDetails <- newFolderChange(newItems, movedItems, modifiedItems)
}()
} else {
log("No change in folder %q", directory)
}
}
folderWatcher.running = false
// capture the entry list for a restart
folderWatcher.captureEntryList(entryList)
// inform channel-subscribers
go func() {
folderWatcher.stopped <- true
}()
// decrement the watch counter
numberOfFolderWatchers--
// final log message
log("Stopped folder watcher %q", folderWatcher.String())
}()
}
func (folderWatcher *FolderWatcher) Stop() {
log("Stopping folder watcher %q", folderWatcher.String())
folderWatcher.wasStopped = true
}
func (folderWatcher *FolderWatcher) IsRunning() bool {
return folderWatcher.running
}
func (folderWatcher *FolderWatcher) getPreviousEntryList() []string {
return folderWatcher.previousEntries
}
// Remember the entry list for a later restart
func (folderWatcher *FolderWatcher) captureEntryList(list []string) {
folderWatcher.previousEntries = list
}
func getFolderEntries(directory string, recurse bool, skipFile func(path string) bool) ([]string, error) {
// the return array
entries := make([]string, 0)
// read the entries of the specified directory
directoryEntries, err := ioutil.ReadDir(directory)
if err != nil {
return entries, err
}
for _, entry := range directoryEntries {
// get the full path
subEntryPath := filepath.Join(directory, entry.Name())
// recurse or append
if recurse && entry.IsDir() {
// recurse (ignore errors, unreadable sub directories don't hurt much)
subFolderEntries, _ := getFolderEntries(subEntryPath, recurse, skipFile)
entries = append(entries, subFolderEntries...)
} else {
// check if the enty shall be ignored
if skipFile(subEntryPath) {
continue
}
// append entry
entries = append(entries, subEntryPath)
}
}
return entries, nil
}