-
Notifications
You must be signed in to change notification settings - Fork 0
/
filewatcher_cached.go
106 lines (89 loc) · 2.42 KB
/
filewatcher_cached.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package fs
import (
"context"
"fmt"
"os"
"path/filepath"
"sync"
"go.uber.org/zap"
)
// CachedFileWatcher is a FileWatcher that caches and tracks the contents
// of watched files.
type CachedFileWatcher interface {
// Start the FileWatcher process.
Start(ctx context.Context)
// Add a file path to be watched.
Add(path string) error
// GetContent of watched file.
GetContent(path string) ([]byte, error)
}
type cachedFileWatcher struct {
cw FileWatcher
watchedFiles map[string][]byte
m sync.RWMutex
logger *zap.SugaredLogger
}
// NewCachedFileWatcher creates a new FileWatcher object that register files
// and calls back when they change.
func NewCachedFileWatcher(logger *zap.SugaredLogger) (CachedFileWatcher, error) {
cw, err := NewWatcher(logger)
if err != nil {
return nil, err
}
return &cachedFileWatcher{
watchedFiles: make(map[string][]byte),
cw: cw,
logger: logger,
}, nil
}
// Start the FileWatcher process.
func (ccw *cachedFileWatcher) Start(ctx context.Context) {
ccw.cw.Start(ctx)
}
// updateContentFromFile does not locks the watchedFiles map, it is up
// to the caller to do so.
// nolint:gocyclo
func (ccw *cachedFileWatcher) updateContentFromFile(path string) error {
content, err := os.ReadFile(filepath.Clean(path))
if err != nil {
return err
}
ccw.watchedFiles[path] = content
return nil
}
func (ccw *cachedFileWatcher) callback(path string) WatchCallback {
return func() {
ccw.m.Lock()
defer ccw.m.Unlock()
if err := ccw.updateContentFromFile(path); err != nil {
ccw.logger.Errorw("Could not read watched file", zap.Error(err))
}
}
}
// Add a file path to be watched.
func (ccw *cachedFileWatcher) Add(path string) error {
if err := ccw.cw.Add(path, ccw.callback(path)); err != nil {
return err
}
ccw.m.Lock()
defer ccw.m.Unlock()
if _, ok := ccw.watchedFiles[path]; !ok {
if err := ccw.updateContentFromFile(path); err != nil {
ccw.logger.Errorw("Could not get content from file", zap.Error(err))
// initialize to be able to distinguish paths not being watched
// and those being watched but not available.
ccw.watchedFiles[path] = nil
}
}
return nil
}
// GetContent of watched file.
func (ccw *cachedFileWatcher) GetContent(path string) ([]byte, error) {
ccw.m.RLock()
defer ccw.m.RUnlock()
content, ok := ccw.watchedFiles[path]
if !ok {
return nil, fmt.Errorf("file %q is not being watched", path)
}
return content, nil
}