-
Notifications
You must be signed in to change notification settings - Fork 0
/
box.go
148 lines (124 loc) · 2.71 KB
/
box.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package pantry
import (
"io/ioutil"
"os"
"path/filepath"
"time"
"context"
"net/http"
"github.com/fsnotify/fsnotify"
)
// Box
type Box interface {
Path() string
Get() ([]byte, error)
Set(data []byte) error
Watch(task func(err error)) error
WatchContext(ctx context.Context, task func(err error)) error
}
type BaseBox struct {
path string
}
func (bb *BaseBox) Path() string {
return bb.path
}
// FileBox
type FileBox struct {
BaseBox
//lock sync.Mutex
//watchDone chan struct{}
}
func NewFileBox(path string) *FileBox {
return &FileBox{BaseBox: BaseBox{filepath.Clean(path)}}
}
func (fb *FileBox) Get() ([]byte, error) {
return ioutil.ReadFile(fb.path)
}
func (fb *FileBox) Set(data []byte) error {
if path := filepath.Dir(fb.path); !pathExists(path) {
if err := os.MkdirAll(path, os.ModePerm); err != nil {
return err
}
}
return ioutil.WriteFile(fb.path, data, os.ModePerm)
}
func (fb *FileBox) Watch(task func(err error)) error {
return fb.WatchContext(context.Background(), task)
}
func (fb *FileBox) WatchContext(ctx context.Context, task func(err error)) error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}
filePath := filepath.Clean(fb.path)
fileDir, _ := filepath.Split(filePath)
if err := watcher.Add(fileDir); err != nil {
return err
}
done := make(chan struct{})
go func() {
defer watcher.Close()
// we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way
go func() {
defer close(done)
ch := make(chan bool)
defer close(ch)
unjitterFunc(ch, time.Millisecond*500, func() { task(nil) })
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
if filepath.Clean(event.Name) == filePath {
if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
ch <- true //task(nil)
}
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
task(err)
case <-ctx.Done():
return
}
}
}()
<-done
}()
return nil
}
// URLBox
type URLBox struct {
url, format string
}
func NewURLBox(url, format string) *URLBox {
return &URLBox{url, format}
}
func (ub *URLBox) Format() string {
if ub.format == "" {
return ext(ub.url)
}
return ub.format
}
func (ub *URLBox) Path() string {
return ub.url
}
func (ub *URLBox) Get() ([]byte, error) {
resp, err := http.Get(ub.url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
func (ub *URLBox) Set(data []byte) error {
return nil
}
func (ub *URLBox) Watch(task func(err error)) error {
return nil
}
func (ub *URLBox) WatchContext(ctx context.Context, task func(err error)) error {
return nil
}