/
index.go
139 lines (112 loc) 路 3.04 KB
/
index.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
// Package index contains files responsible for maintaining the database document index
package index
import (
"fmt"
"sync"
"time"
"github.com/jackyzha0/nanoDB/log"
af "github.com/spf13/afero"
)
// I is the global database index which keeps track of
// which files are where
var I *FileIndex
// NewFileIndex returns a reference to a new file index
func NewFileIndex(dir string) *FileIndex {
return &FileIndex{
dir: dir,
index: map[string]*File{},
FileSystem: af.NewOsFs(),
}
}
// FileIndex is holds the actual index mapping for keys to files
type FileIndex struct {
mu sync.RWMutex
dir string
index map[string]*File
FileSystem af.Fs
}
// File stores the filename as well as a read-write mutex
type File struct {
FileName string
mu sync.RWMutex
}
// SetFileSystem sets the file system for the given FileIndex
func (i *FileIndex) SetFileSystem(fs af.Fs) {
i.FileSystem = fs
}
// List returns all keys in database
func (i *FileIndex) List() (res []string) {
// read lock on index
i.mu.RLock()
defer i.mu.RUnlock()
for k := range i.index {
res = append(res, k)
}
return res
}
// Lookup returns the file with that key
// Returns (File, true) if file exists
// otherwise, returns new File, false
func (i *FileIndex) Lookup(key string) (*File, bool) {
// read lock on index
i.mu.RLock()
defer i.mu.RUnlock()
// get if File exists, return nil and false otherwise
if file, ok := i.index[key]; ok {
return file, true
}
return &File{FileName: key}, false
}
// Put creates/updates file in the fileindex
func (i *FileIndex) Put(file *File, bytes []byte) error {
// write lock on index
i.mu.Lock()
defer i.mu.Unlock()
i.index[file.FileName] = file
err := file.ReplaceContent(string(bytes))
return err
}
// ResolvePath returns a string representing the path to file
func (f *File) ResolvePath() string {
if I.dir == "" {
return fmt.Sprintf("%s.json", f.FileName)
}
return fmt.Sprintf("%s/%s.json", I.dir, f.FileName)
}
// Regenerate rebuilds the current file index from current directory
// by crawling it for any .json files
func (i *FileIndex) Regenerate() {
// write lock on index
i.mu.Lock()
defer i.mu.Unlock()
start := time.Now()
log.Info("building index for directory %s...", i.dir)
i.index = i.buildIndexMap()
log.Success("built index of %d files in %d ms", len(i.index), time.Since(start).Milliseconds())
}
// RegenerateNew rebuilds the file index at a new given directory
func (i *FileIndex) RegenerateNew(dir string) {
i.dir = dir
i.Regenerate()
}
// creates a map from key to File
func (i *FileIndex) buildIndexMap() map[string]*File {
newIndexMap := make(map[string]*File)
files := crawlDirectory(i.dir)
for _, f := range files {
newIndexMap[f] = &File{FileName: f}
}
return newIndexMap
}
// Delete deletes the given file and then removes it from I
func (i *FileIndex) Delete(file *File) error {
// write lock on index
i.mu.Lock()
defer i.mu.Unlock()
// delete first so pointer isn't nil
err := file.Delete()
if err == nil {
delete(i.index, file.FileName)
}
return err
}