-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathfiledb.go
157 lines (126 loc) · 3.41 KB
/
filedb.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
149
150
151
152
153
154
155
156
157
// Copyright 2014 Gyepi Sam. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package redux
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/gyepisam/fileutils"
)
const (
// Where is data kept?
DATA_DIR = "data"
)
// FileDb is a file based DB for storing Redo relationships and metadata.
type FileDb struct {
DataDir string
}
var NullKeyErr = errors.New("Key cannot be empty.")
var NullPrefixErr = errors.New("Prefix cannot be empty.")
// Open requires a project root argument
func FileDbOpen(rootdir string) (DB, error) {
redodir := filepath.Join(rootdir, REDO_DIR)
if exists, err := fileutils.DirExists(redodir); err != nil {
return nil, err
} else if !exists {
return nil, fmt.Errorf("FileDb redo directory [%s] not found. Forget to call redo-init?", redodir)
}
datadir := filepath.Join(redodir, DATA_DIR)
if err := os.Mkdir(datadir, DIR_PERM); err != nil && !os.IsExist(err) {
return nil, fmt.Errorf("FileDb cannot make data directory [%s]. %s", datadir, err)
}
return &FileDb{DataDir: datadir}, nil
}
func (db *FileDb) IsNull() bool { return false }
func (db *FileDb) makePath(key string) string {
return filepath.Join(db.DataDir, key)
}
// Close is a noop for this DB type
func (db *FileDb) Close() error {
return nil
}
func (db *FileDb) Put(key string, value []byte) error {
if len(key) == 0 {
return NullKeyErr
}
return fileutils.AtomicWrite(db.makePath(key), func(tmpFile *os.File) error {
_, err := tmpFile.Write(value)
return err
})
}
func (db *FileDb) Get(key string) ([]byte, bool, error) {
if len(key) == 0 {
return nil, false, NullKeyErr
}
b, err := ioutil.ReadFile(db.makePath(key))
var found bool
if err == nil {
found = true
} else if os.IsNotExist(err) {
found = false //explicit is better than implicit
err = nil
}
return b, found, err
}
func (db *FileDb) Delete(key string) error {
if len(key) == 0 {
return NullKeyErr
}
err := os.Remove(db.makePath(key))
if err != nil && os.IsNotExist(err) {
return nil
}
return err
}
func (db *FileDb) GetRecords(prefix string) ([]Record, error) {
if len(prefix) == 0 {
return nil, NullPrefixErr
}
var out []Record
rootLen := len(db.DataDir) + 1
walker := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
key := path[rootLen:]
// Go 1.0.x compatible syntax for info.Mode().IsRegular()
if isRegular := info.Mode()&os.ModeType == 0; isRegular && strings.HasPrefix(key, prefix) {
if b, err := ioutil.ReadFile(path); err != nil {
return err
} else {
out = append(out, Record{Key: key, Value: b})
}
}
return nil
}
return out, filepath.Walk(db.DataDir+"/", walker)
}
// GetKeys returns an array of keys that are prefixes of the specified key.
func (db *FileDb) GetKeys(prefix string) ([]string, error) {
records, err := db.GetRecords(prefix)
if err != nil {
return nil, err
}
out := make([]string, len(records))
for i, rec := range records {
out[i] = rec.Key
}
return out, nil
}
// GetValues returns an array of data values for keys with the specified prefix.
func (db *FileDb) GetValues(prefix string) ([][]byte, error) {
records, err := db.GetRecords(prefix)
if err != nil {
return nil, err
}
out := make([][]byte, len(records))
for i, rec := range records {
out[i] = make([]byte, len(rec.Value))
copy(out[i], rec.Value)
}
return out, nil
}