mirror of
https://github.com/Luzifer/go_helpers.git
synced 2024-12-26 05:51:20 +00:00
146 lines
3.7 KiB
Go
146 lines
3.7 KiB
Go
|
package file
|
||
|
|
||
|
import (
|
||
|
"crypto/sha512"
|
||
|
"fmt"
|
||
|
"hash"
|
||
|
"io"
|
||
|
"io/fs"
|
||
|
"os"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
keyWatcherCheckHash = "WatcherCheckHash"
|
||
|
keyWatcherCheckMtime = "WatcherCheckMtime"
|
||
|
keyWatcherCheckPresence = "WatcherCheckPresence"
|
||
|
keyWatcherCheckSize = "WatcherCheckSize"
|
||
|
)
|
||
|
|
||
|
// WatcherCheckHash returns a WatcherCheck configured with the given
|
||
|
// hash method (i.e. provide md5.New, sha1.New, ...). If the file is
|
||
|
// not present at the time of the check the check is skipped and will
|
||
|
// NOT cause an error.
|
||
|
func WatcherCheckHash(hcf func() hash.Hash) WatcherCheck {
|
||
|
return func(w *Watcher) (WatcherEvent, error) {
|
||
|
var lastHash string
|
||
|
if v, ok := w.GetState(keyWatcherCheckHash).(string); ok {
|
||
|
lastHash = v
|
||
|
}
|
||
|
|
||
|
if _, err := os.Stat(w.FilePath); errors.Is(err, fs.ErrNotExist) {
|
||
|
return WatcherEventInvalid, nil
|
||
|
}
|
||
|
|
||
|
f, err := os.Open(w.FilePath)
|
||
|
if err != nil {
|
||
|
return WatcherEventInvalid, errors.Wrap(err, "opening file")
|
||
|
}
|
||
|
defer f.Close()
|
||
|
|
||
|
h := hcf()
|
||
|
if _, err = io.Copy(h, f); err != nil {
|
||
|
return WatcherEventInvalid, errors.Wrap(err, "reading file")
|
||
|
}
|
||
|
|
||
|
currentHash := fmt.Sprintf("%x", h.Sum(nil))
|
||
|
if lastHash == currentHash {
|
||
|
return WatcherEventNoChange, nil
|
||
|
}
|
||
|
|
||
|
w.SetState(keyWatcherCheckHash, currentHash)
|
||
|
return WatcherEventFileModified, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var _ WatcherCheck = WatcherCheckHash(sha512.New)
|
||
|
|
||
|
// WatcherCheckMtime checks whether the mtime attribute of the file
|
||
|
// has changed. If the file is not present at the time of the check
|
||
|
// the check is skipped and will NOT cause an error.
|
||
|
func WatcherCheckMtime(w *Watcher) (WatcherEvent, error) {
|
||
|
var lastChange int64
|
||
|
if v, ok := w.GetState(keyWatcherCheckMtime).(int64); ok {
|
||
|
lastChange = v
|
||
|
}
|
||
|
|
||
|
s, err := os.Stat(w.FilePath)
|
||
|
switch {
|
||
|
case err == nil:
|
||
|
// handle size change
|
||
|
case errors.Is(err, fs.ErrNotExist):
|
||
|
return WatcherEventInvalid, nil
|
||
|
default:
|
||
|
return WatcherEventInvalid, errors.Wrap(err, "getting file stat")
|
||
|
}
|
||
|
|
||
|
if s.ModTime().UnixNano() == lastChange {
|
||
|
return WatcherEventNoChange, nil
|
||
|
}
|
||
|
|
||
|
w.SetState(keyWatcherCheckMtime, s.ModTime().UnixNano())
|
||
|
return WatcherEventFileModified, nil
|
||
|
}
|
||
|
|
||
|
var _ WatcherCheck = WatcherCheckMtime
|
||
|
|
||
|
// WatcherCheckPresence simply checks whether the file is present and
|
||
|
// allows to emit WatcherEventFileAppeared / WatcherEventFileVanished
|
||
|
// events when the file existence state changes.
|
||
|
func WatcherCheckPresence(w *Watcher) (WatcherEvent, error) {
|
||
|
var wasPresent bool
|
||
|
if v, ok := w.GetState(keyWatcherCheckPresence).(bool); ok {
|
||
|
wasPresent = v
|
||
|
}
|
||
|
|
||
|
_, err := os.Stat(w.FilePath)
|
||
|
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||
|
// Some weird error occurred
|
||
|
return WatcherEventInvalid, errors.Wrap(err, "getting file stat")
|
||
|
}
|
||
|
|
||
|
isPresent := err == nil
|
||
|
w.SetState(keyWatcherCheckPresence, isPresent)
|
||
|
|
||
|
switch {
|
||
|
case !wasPresent && isPresent:
|
||
|
return WatcherEventFileAppeared, nil
|
||
|
case wasPresent && !isPresent:
|
||
|
return WatcherEventFileVanished, nil
|
||
|
default:
|
||
|
return WatcherEventNoChange, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var _ WatcherCheck = WatcherCheckPresence
|
||
|
|
||
|
// WatcherCheckSize checks whether the size of the file has changed.
|
||
|
// If the file is not present at the time of the check the check is
|
||
|
// skipped and will NOT cause an error.
|
||
|
func WatcherCheckSize(w *Watcher) (WatcherEvent, error) {
|
||
|
var knownSize int64
|
||
|
if v, ok := w.GetState(keyWatcherCheckSize).(int64); ok {
|
||
|
knownSize = v
|
||
|
}
|
||
|
|
||
|
s, err := os.Stat(w.FilePath)
|
||
|
switch {
|
||
|
case err == nil:
|
||
|
// handle size change
|
||
|
case errors.Is(err, fs.ErrNotExist):
|
||
|
return WatcherEventInvalid, nil
|
||
|
default:
|
||
|
return WatcherEventInvalid, errors.Wrap(err, "getting file stat")
|
||
|
}
|
||
|
|
||
|
if s.Size() == knownSize {
|
||
|
return WatcherEventNoChange, nil
|
||
|
}
|
||
|
|
||
|
w.SetState(keyWatcherCheckSize, s.Size())
|
||
|
return WatcherEventFileModified, nil
|
||
|
}
|
||
|
|
||
|
var _ WatcherCheck = WatcherCheckSize
|