-
Notifications
You must be signed in to change notification settings - Fork 721
/
checkpoint.go
113 lines (95 loc) · 2.59 KB
/
checkpoint.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
package datastore
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
)
// Checkpointer can persist data and (hopefully) restore it later
type Checkpointer interface {
Checkpoint(data interface{}) error
Restore(into interface{}) error
}
// NullCheckpoint discards data and always returns "not found". For testing only!
type NullCheckpoint struct{}
// Checkpoint implements the Checkpointer interface in the most
// trivial sense, by just discarding data.
func (c NullCheckpoint) Checkpoint(data interface{}) error {
return nil
}
// Restore implements the Checkpointer interface in the most trivial
// sense, by always returning "not found".
func (c NullCheckpoint) Restore(into interface{}) error {
return os.ErrNotExist
}
// TestCheckpoint maintains a snapshot in memory.
type TestCheckpoint struct {
Error error
Data interface{}
}
// NewTestCheckpoint creates a new TestCheckpoint.
func NewTestCheckpoint(data interface{}) *TestCheckpoint {
return &TestCheckpoint{Data: data}
}
// Checkpoint implements the Checkpointer interface.
func (c *TestCheckpoint) Checkpoint(data interface{}) error {
if c.Error != nil {
return c.Error
}
c.Data = data
return nil
}
// Restore implements the Checkpointer interface.
func (c *TestCheckpoint) Restore(into interface{}) error {
if c.Error != nil {
return c.Error
}
// `into` is always a pointer to interface{}, but we can't
// actually make the Restore() function *interface{}, because
// that doesn't match the (widely used) `encoding.Unmarshal`
// interface :(
// Round trip through json strings instead because copying is
// hard.
buf, err := json.Marshal(c.Data)
if err != nil {
return err
}
return json.Unmarshal(buf, into)
}
// JSONFile is a checkpointer that writes to a JSON file
type JSONFile struct {
path string
}
// NewJSONFile creates a new JsonFile
func NewJSONFile(path string) *JSONFile {
return &JSONFile{path: path}
}
// Checkpoint implements the Checkpointer interface
func (c *JSONFile) Checkpoint(data interface{}) error {
f, err := ioutil.TempFile(filepath.Dir(c.path), filepath.Base(c.path)+".tmp*")
if err != nil {
return err
}
if err := json.NewEncoder(f).Encode(&data); err != nil {
os.Remove(f.Name())
return err
}
if err := f.Sync(); err != nil {
os.Remove(f.Name())
return err
}
if err := os.Rename(f.Name(), c.path); err != nil {
os.Remove(f.Name())
return err
}
return nil
}
// Restore implements the Checkpointer interface
func (c *JSONFile) Restore(into interface{}) error {
f, err := os.Open(c.path)
if err != nil {
return err
}
defer f.Close()
return json.NewDecoder(f).Decode(into)
}