192 lines
3.8 KiB
Go
192 lines
3.8 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"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
var numberOfFileWatchers int
|
|
|
|
func init() {
|
|
numberOfFolderWatchers = 0
|
|
}
|
|
|
|
func NumberOfFileWatchers() int {
|
|
return numberOfFileWatchers
|
|
}
|
|
|
|
type FileWatcher struct {
|
|
modified chan bool
|
|
moved chan bool
|
|
stopped chan bool
|
|
|
|
file string
|
|
running bool
|
|
wasStopped bool
|
|
checkInterval time.Duration
|
|
|
|
previousModTime time.Time
|
|
}
|
|
|
|
func NewFileWatcher(filePath string, checkIntervalInSeconds int) *FileWatcher {
|
|
|
|
if checkIntervalInSeconds < 1 {
|
|
panic(fmt.Sprintf("Cannot create a file watcher with a check interval of %v seconds.", checkIntervalInSeconds))
|
|
}
|
|
|
|
return &FileWatcher{
|
|
modified: make(chan bool),
|
|
moved: make(chan bool),
|
|
stopped: make(chan bool),
|
|
|
|
file: filePath,
|
|
checkInterval: time.Duration(checkIntervalInSeconds),
|
|
}
|
|
}
|
|
|
|
func (fileWatcher *FileWatcher) String() string {
|
|
return fmt.Sprintf("Filewatcher %q", fileWatcher.file)
|
|
}
|
|
|
|
func (fileWatcher *FileWatcher) SetFile(filePath string) {
|
|
fileWatcher.file = filePath
|
|
}
|
|
|
|
func (filewatcher *FileWatcher) Modified() chan bool {
|
|
return filewatcher.modified
|
|
}
|
|
|
|
func (filewatcher *FileWatcher) Moved() chan bool {
|
|
return filewatcher.moved
|
|
}
|
|
|
|
func (filewatcher *FileWatcher) Stopped() chan bool {
|
|
return filewatcher.stopped
|
|
}
|
|
|
|
func (fileWatcher *FileWatcher) Start() {
|
|
fileWatcher.running = true
|
|
sleepInterval := time.Second * fileWatcher.checkInterval
|
|
|
|
go func() {
|
|
|
|
// increment watcher count
|
|
numberOfFileWatchers++
|
|
|
|
var modTime time.Time
|
|
previousModTime := fileWatcher.getPreviousModTime()
|
|
|
|
if timeIsSet(previousModTime) {
|
|
modTime = previousModTime
|
|
} else {
|
|
currentModTime, err := getLastModTimeFromFile(fileWatcher.file)
|
|
if err != nil {
|
|
|
|
// send out the notification
|
|
log("File %q has been moved or is inaccessible.", fileWatcher.file)
|
|
go func() {
|
|
fileWatcher.moved <- true
|
|
}()
|
|
|
|
// stop this file watcher
|
|
fileWatcher.Stop()
|
|
|
|
} else {
|
|
|
|
modTime = currentModTime
|
|
}
|
|
|
|
}
|
|
|
|
for fileWatcher.wasStopped == false {
|
|
|
|
newModTime, err := getLastModTimeFromFile(fileWatcher.file)
|
|
if err != nil {
|
|
|
|
// send out the notification
|
|
log("File %q has been moved.", fileWatcher.file)
|
|
go func() {
|
|
fileWatcher.moved <- true
|
|
}()
|
|
|
|
// stop this file watcher
|
|
fileWatcher.Stop()
|
|
|
|
continue
|
|
}
|
|
|
|
// detect changes
|
|
if modTime.Before(newModTime) {
|
|
|
|
// send out the notification
|
|
log("File %q has been modified.", fileWatcher.file)
|
|
go func() {
|
|
fileWatcher.modified <- true
|
|
}()
|
|
|
|
} else {
|
|
|
|
log("File %q has not changed.", fileWatcher.file)
|
|
|
|
}
|
|
|
|
// assign the new modtime
|
|
modTime = newModTime
|
|
|
|
time.Sleep(sleepInterval)
|
|
|
|
}
|
|
|
|
fileWatcher.running = false
|
|
|
|
// capture the entry list for a restart
|
|
fileWatcher.captureModTime(modTime)
|
|
|
|
// inform channel-subscribers
|
|
go func() {
|
|
fileWatcher.stopped <- true
|
|
}()
|
|
|
|
// decrement the watch counter
|
|
numberOfFileWatchers--
|
|
|
|
// final log message
|
|
log("Stopped file watcher %q", fileWatcher.String())
|
|
}()
|
|
}
|
|
|
|
func (fileWatcher *FileWatcher) Stop() {
|
|
log("Stopping file watcher %q", fileWatcher.String())
|
|
fileWatcher.wasStopped = true
|
|
}
|
|
|
|
func (fileWatcher *FileWatcher) IsRunning() bool {
|
|
return fileWatcher.running
|
|
}
|
|
|
|
func (fileWatcher *FileWatcher) getPreviousModTime() time.Time {
|
|
return fileWatcher.previousModTime
|
|
}
|
|
|
|
// Remember the last mod time for a later restart
|
|
func (fileWatcher *FileWatcher) captureModTime(modTime time.Time) {
|
|
fileWatcher.previousModTime = modTime
|
|
}
|
|
|
|
func getLastModTimeFromFile(file string) (time.Time, error) {
|
|
fileInfo, err := os.Stat(file)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
|
|
return fileInfo.ModTime(), nil
|
|
}
|
|
|
|
func timeIsSet(t time.Time) bool {
|
|
return time.Time{} == t
|
|
}
|