65 lines
1.3 KiB
Go
65 lines
1.3 KiB
Go
package quota
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"sync"
|
|
)
|
|
|
|
// fileStore persists all records into one JSON file. A single instance is shared
|
|
// per path so concurrent per-name saves merge instead of clobbering each other.
|
|
type fileStore struct {
|
|
path string
|
|
mu sync.Mutex
|
|
data map[string]Record
|
|
}
|
|
|
|
var fileStores sync.Map // path -> *fileStore
|
|
|
|
func NewFileStore(path string) Store {
|
|
if v, ok := fileStores.Load(path); ok {
|
|
return v.(*fileStore)
|
|
}
|
|
fs := &fileStore{path: path, data: make(map[string]Record)}
|
|
fs.load()
|
|
actual, _ := fileStores.LoadOrStore(path, fs)
|
|
return actual.(*fileStore)
|
|
}
|
|
|
|
func (fs *fileStore) load() {
|
|
b, err := os.ReadFile(fs.path)
|
|
if err != nil {
|
|
return
|
|
}
|
|
data := make(map[string]Record)
|
|
if json.Unmarshal(b, &data) == nil {
|
|
fs.data = data
|
|
}
|
|
}
|
|
|
|
func (fs *fileStore) Load(name string) (Record, bool, error) {
|
|
fs.mu.Lock()
|
|
defer fs.mu.Unlock()
|
|
rec, ok := fs.data[name]
|
|
return rec, ok, nil
|
|
}
|
|
|
|
func (fs *fileStore) Save(name string, rec Record) error {
|
|
fs.mu.Lock()
|
|
defer fs.mu.Unlock()
|
|
fs.data[name] = rec
|
|
return fs.writeLocked()
|
|
}
|
|
|
|
func (fs *fileStore) writeLocked() error {
|
|
b, err := json.MarshalIndent(fs.data, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tmp := fs.path + ".tmp"
|
|
if err := os.WriteFile(tmp, b, 0o644); err != nil {
|
|
return err
|
|
}
|
|
return os.Rename(tmp, fs.path)
|
|
}
|