mirror of
https://github.com/datarhei/core.git
synced 2025-09-27 04:16:25 +08:00
148 lines
2.6 KiB
Go
148 lines
2.6 KiB
Go
package store
|
|
|
|
import (
|
|
gojson "encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"sync"
|
|
|
|
"github.com/datarhei/core/v16/encoding/json"
|
|
"github.com/datarhei/core/v16/io/file"
|
|
"github.com/datarhei/core/v16/log"
|
|
)
|
|
|
|
type JSONConfig struct {
|
|
Filepath string
|
|
FFVersion string
|
|
Logger log.Logger
|
|
}
|
|
|
|
type jsonStore struct {
|
|
filepath string
|
|
ffversion string
|
|
logger log.Logger
|
|
|
|
// Mutex to serialize access to the backend
|
|
lock sync.RWMutex
|
|
}
|
|
|
|
var version uint64 = 4
|
|
|
|
func NewJSONStore(config JSONConfig) Store {
|
|
s := &jsonStore{
|
|
filepath: config.Filepath,
|
|
ffversion: config.FFVersion,
|
|
logger: config.Logger,
|
|
}
|
|
|
|
if s.logger == nil {
|
|
s.logger = log.New("")
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func (s *jsonStore) Load() (StoreData, error) {
|
|
s.lock.Lock()
|
|
defer s.lock.Unlock()
|
|
|
|
data, err := s.load(s.filepath, version)
|
|
if err != nil {
|
|
return NewStoreData(), err
|
|
}
|
|
|
|
data.sanitize()
|
|
|
|
return data, nil
|
|
}
|
|
|
|
func (s *jsonStore) Store(data StoreData) error {
|
|
if data.Version != version {
|
|
return fmt.Errorf("invalid version (have: %d, want: %d)", data.Version, version)
|
|
}
|
|
|
|
s.lock.RLock()
|
|
defer s.lock.RUnlock()
|
|
|
|
err := s.store(s.filepath, data)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to store data: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *jsonStore) store(filepath string, data StoreData) error {
|
|
jsondata, err := gojson.MarshalIndent(&data, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dir := path.Dir(filepath)
|
|
name := path.Base(filepath)
|
|
|
|
tmpfile, err := os.CreateTemp(dir, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer os.Remove(tmpfile.Name())
|
|
|
|
if _, err := tmpfile.Write(jsondata); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tmpfile.Close(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := file.Rename(tmpfile.Name(), filepath); err != nil {
|
|
return err
|
|
}
|
|
|
|
s.logger.WithField("file", filepath).Debug().Log("Stored data")
|
|
|
|
return nil
|
|
}
|
|
|
|
type storeVersion struct {
|
|
Version uint64 `json:"version"`
|
|
}
|
|
|
|
func (s *jsonStore) load(filepath string, version uint64) (StoreData, error) {
|
|
r := NewStoreData()
|
|
|
|
_, err := os.Stat(filepath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return r, nil
|
|
}
|
|
|
|
return r, err
|
|
}
|
|
|
|
jsondata, err := os.ReadFile(filepath)
|
|
if err != nil {
|
|
return r, err
|
|
}
|
|
|
|
var db storeVersion
|
|
|
|
if err = gojson.Unmarshal(jsondata, &db); err != nil {
|
|
return r, json.FormatError(jsondata, err)
|
|
}
|
|
|
|
if db.Version != version {
|
|
return r, fmt.Errorf("unsupported version of the DB file (want: %d, have: %d)", version, db.Version)
|
|
}
|
|
|
|
if err = gojson.Unmarshal(jsondata, &r); err != nil {
|
|
return r, json.FormatError(jsondata, err)
|
|
}
|
|
|
|
s.logger.WithField("file", filepath).Debug().Log("Read data")
|
|
|
|
return r, nil
|
|
}
|