refactor: разнести пакеты и обновить валидацию

This commit is contained in:
Георгий
2026-07-14 22:00:34 +03:00
parent 3ed29fa8e6
commit 1e1008d506
20 changed files with 409 additions and 128 deletions

86
storage/file.go Normal file
View File

@@ -0,0 +1,86 @@
package storage
import (
"context"
"encoding/json"
"errors"
"os"
"path/filepath"
"sync"
"gitea.unprism.ru/KRBL/mila/protocol"
)
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) (protocol.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 protocol.ContentState{}, protocol.ErrNotReady
}
return protocol.ContentState{}, err
}
var state protocol.ContentState
if err := json.Unmarshal(data, &state); err != nil {
return protocol.ContentState{}, err
}
if state.IsZero() {
return protocol.ContentState{}, protocol.ErrNotReady
}
if err := s.mem.Set(ctx, state); err != nil {
return protocol.ContentState{}, err
}
return state, nil
}
func (s *FileStore) Set(ctx context.Context, state protocol.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)
}

47
storage/file_test.go Normal file
View File

@@ -0,0 +1,47 @@
package storage
import (
"context"
"errors"
"path/filepath"
"testing"
"gitea.unprism.ru/KRBL/mila/protocol"
)
func TestFileStorePersistsAndLoadsState(t *testing.T) {
path := filepath.Join(t.TempDir(), "bvk_content.json")
ctx := context.Background()
first := NewFileStore(path)
state := protocol.ContentState{
Route: "3",
Text: "Гостиный двор",
Template: "main",
}
if err := first.Set(ctx, state); err != nil {
t.Fatalf("set state: %v", err)
}
second := NewFileStore(path)
got, err := second.Get(ctx)
if err != nil {
t.Fatalf("get state: %v", err)
}
if got.Route != state.Route || got.Text != state.Text || got.Template != state.Template {
t.Fatalf("unexpected state: %+v", got)
}
}
func TestFileStoreMissingFileReturnsNotReady(t *testing.T) {
store := NewFileStore(filepath.Join(t.TempDir(), "missing.json"))
_, err := store.Get(context.Background())
if err == nil {
t.Fatal("expected error")
}
if !errors.Is(err, protocol.ErrNotReady) {
t.Fatalf("expected ErrNotReady, got %v", err)
}
}

49
storage/memory.go Normal file
View File

@@ -0,0 +1,49 @@
package storage
import (
"context"
"sync"
"gitea.unprism.ru/KRBL/mila/protocol"
)
type MemoryStore struct {
mu sync.RWMutex
state protocol.ContentState
ready bool
}
func NewMemoryStore() *MemoryStore {
return &MemoryStore{}
}
func (s *MemoryStore) Get(ctx context.Context) (protocol.ContentState, error) {
select {
case <-ctx.Done():
return protocol.ContentState{}, ctx.Err()
default:
}
s.mu.RLock()
defer s.mu.RUnlock()
if !s.ready {
return protocol.ContentState{}, protocol.ErrNotReady
}
return s.state, nil
}
func (s *MemoryStore) Set(ctx context.Context, state protocol.ContentState) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
s.mu.Lock()
defer s.mu.Unlock()
s.state = state
s.ready = true
return nil
}