-
Notifications
You must be signed in to change notification settings - Fork 0
/
api.go
101 lines (87 loc) · 2.03 KB
/
api.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
package extremofile
import (
"io"
"os"
"path/filepath"
)
const DefaultDirPerm os.FileMode = 0700
const DefaultFilePerm os.FileMode = 0600
const DefaultFilePrefix = "extremofile."
type Config struct {
Dir string
FilePrefix string
DirPerm os.FileMode
FilePerm os.FileMode
MustExist bool
ReadOnly bool
}
// Does not perform IO.
func New(config Config) *efile {
e := &efile{config: &config}
if e.config.FilePrefix == "" {
e.config.FilePrefix = DefaultFilePrefix
}
if e.config.FilePerm == 0 {
e.config.FilePerm = DefaultFilePerm
}
if e.config.DirPerm == 0 {
e.config.DirPerm = DefaultDirPerm
}
e.pathMain = filepath.Join(e.config.Dir, e.config.FilePrefix+"v1.main")
e.pathBackup = filepath.Join(e.config.Dir, e.config.FilePrefix+"v1.backup")
return e
}
// Open is simpler API, checks write access.
// Bytes and Writer may be not nil together with error to indicate non fatal issues.
func Open(dir string) ([]byte, io.Writer, error) {
e := New(Config{Dir: dir})
if err := e.mkdir(); IsCritical(err) {
return nil, nil, err
}
data, readErr := e.read()
if IsCritical(readErr) {
return nil, nil, readErr
}
return data, e, readErr
}
func (e *efile) Read() ([]byte, error) {
e.Lock()
defer e.Unlock()
if err := e.mkdir(); IsCritical(err) {
return nil, err
}
data, err := e.read()
if IsCritical(err) {
return nil, err
}
return data, err
}
// Write returns after write, backup and sync. After success, `.Bytes()` will return new data.
// Write is atomic and thread-safe.
func (e *efile) Write(b []byte) (int, error) {
e.Lock()
defer e.Unlock()
if err := e.mkdir(); IsCritical(err) {
return 0, err
}
n := len(b)
err := e.write(b)
if err != nil {
return 0, err
}
return n, nil
}
// storage is not available, most commonly: disk is full or fs permission error
func IsCritical(e error) bool {
if _, ok := e.(critical); ok {
return true
}
return false
}
// data is corrupted, restore is not possible
func IsCorrupt(e error) bool {
if x, ok := e.(critical); ok {
e = x.E
}
return e == errCorrupt
}