85 lines
1.4 KiB
Go
85 lines
1.4 KiB
Go
package mila
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
)
|
|
|
|
type FileStore struct {
|
|
path string
|
|
mu sync.Mutex
|
|
mem *MemoryStore
|
|
}
|
|
|
|
func NewFileStore(path string) *FileStore {
|
|
return &FileStore{
|
|
path: path,
|
|
mem: NewMemoryStore(),
|
|
}
|
|
}
|
|
|
|
func (s *FileStore) Get(ctx context.Context) (ContentState, error) {
|
|
if state, err := s.mem.Get(ctx); err == nil {
|
|
return state, nil
|
|
}
|
|
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
data, err := os.ReadFile(s.path)
|
|
if err != nil {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return ContentState{}, ErrNotReady
|
|
}
|
|
return ContentState{}, err
|
|
}
|
|
|
|
var state ContentState
|
|
if err := json.Unmarshal(data, &state); err != nil {
|
|
return ContentState{}, err
|
|
}
|
|
if state.IsZero() {
|
|
return ContentState{}, ErrNotReady
|
|
}
|
|
|
|
if err := s.mem.Set(ctx, state); err != nil {
|
|
return ContentState{}, err
|
|
}
|
|
return state, nil
|
|
}
|
|
|
|
func (s *FileStore) Set(ctx context.Context, state ContentState) error {
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
default:
|
|
}
|
|
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if err := os.MkdirAll(filepath.Dir(s.path), 0o755); err != nil {
|
|
return err
|
|
}
|
|
|
|
data, err := json.MarshalIndent(state, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tmp := s.path + ".tmp"
|
|
if err := os.WriteFile(tmp, data, 0o644); err != nil {
|
|
return err
|
|
}
|
|
if err := os.Rename(tmp, s.path); err != nil {
|
|
_ = os.Remove(tmp)
|
|
return err
|
|
}
|
|
|
|
return s.mem.Set(ctx, state)
|
|
}
|