package store import ( gojson "encoding/json" "fmt" "os" "sync" "github.com/datarhei/core/v16/encoding/json" "github.com/datarhei/core/v16/io/file" "github.com/datarhei/core/v16/log" ) type JSONConfig struct { Dir string Logger log.Logger } type jsonStore struct { filename string dir 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{ filename: "db.json", dir: config.Dir, logger: config.Logger, } if s.logger == nil { s.logger = log.New("JSONStore") } return s } func (s *jsonStore) Load() (StoreData, error) { s.lock.Lock() defer s.lock.Unlock() data, err := s.load(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(data) if err != nil { return fmt.Errorf("failed to store data: %w", err) } return nil } func (s *jsonStore) store(data StoreData) error { jsondata, err := gojson.MarshalIndent(&data, "", " ") if err != nil { return err } tmpfile, err := os.CreateTemp(s.dir, s.filename) 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 } filename := s.dir + "/" + s.filename if err := file.Rename(tmpfile.Name(), filename); err != nil { return err } s.logger.WithField("file", filename).Debug().Log("Stored data") return nil } type storeVersion struct { Version uint64 `json:"version"` } func (s *jsonStore) load(version uint64) (StoreData, error) { r := NewStoreData() filename := s.dir + "/" + s.filename _, err := os.Stat(filename) if err != nil { if os.IsNotExist(err) { return r, nil } return r, err } jsondata, err := os.ReadFile(filename) 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", filename).Debug().Log("Read data") return r, nil }