refactor: storage interface

This commit is contained in:
Ilia Denisov
2026-03-13 17:30:39 +02:00
parent d7e12cbee0
commit 43039a79bf
3 changed files with 61 additions and 42 deletions
+57 -12
View File
@@ -1,30 +1,75 @@
// fs implements galaxy/storage.Storage with filesystem
package fs
/*
Общие правила:
1. Все хранимые объекты сериализуются / десериализуются как JSON.
2. Структура хранения файлов:
- storageRoot \
|
+-- state.dat
|
+-- {GameID} \
| |
| +-- {Turn}.dat (client.GameData)
| +-- {Turn}.dat (client.GameData)
| +-- ...
|
+-- {GameID} \
|
+-- ...
*/
import (
"fmt"
"galaxy/util"
"path/filepath"
)
type fsStorage struct {
path string
const (
// Name of the file under the storage's root where [model.State] is stored.
stateFileName = "state.dat"
// Suffix of a Game's file inder the storage's root where [model.GameData] is stored.
gameDataFileSuffix = ".dat"
)
// StateFilePath returns client's state file path relative to the root,
// file name and extension are pre-defined constant.
func StateFilePath(root string) string {
return filepath.Join(root, stateFileName)
}
// NewFS returns on-filesystem implementation of the "galaxy/storage.Storage" with root located at rootPath.
// rootPath must me a directory and has write access to the current user. If initial checks failed, return nil and non-nil error.
func NewStorage(path string) (*fsStorage, error) {
if ok, err := util.PathExists(path, true); err != nil {
return nil, fmt.Errorf("new storage: check path %q exists: %w", path, err)
// GameDataPath returns game's data file path relative to the root,
// data file name is GameID string representation and extension is a pre-defined constant.
func GameDataFilePath(root string, id fmt.Stringer) string {
return filepath.Join(root, id.String()) + gameDataFileSuffix
}
type fsStorage struct {
storageRoot string
}
// NewFS returns on-filesystem implementation of the "galaxy/storage.Storage" with root located at storageRoot.
// storageRoot must me a directory and has write access to the current user. If initial checks failed, return nil and non-nil error.
func NewStorage(storageRoot string) (*fsStorage, error) {
if ok, err := util.PathExists(storageRoot, true); err != nil {
return nil, fmt.Errorf("new storage: check path %q exists: %w", storageRoot, err)
} else if !ok {
return nil, fmt.Errorf("new storage: path %q does not exists", path)
return nil, fmt.Errorf("new storage: path %q does not exists", storageRoot)
}
if ok, err := util.Writable(path); err != nil {
return nil, fmt.Errorf("new storage: check path %q writable: %w", path, err)
if ok, err := util.Writable(storageRoot); err != nil {
return nil, fmt.Errorf("new storage: check path %q writable: %w", storageRoot, err)
} else if !ok {
return nil, fmt.Errorf("new storage: path %q is not writable", path)
return nil, fmt.Errorf("new storage: path %q is not writable", storageRoot)
}
s := &fsStorage{
path: path,
storageRoot: storageRoot,
}
return s, nil
}
+4 -4
View File
@@ -37,16 +37,16 @@ type UIStorage interface {
// otherwise callback func accepts loaded [report.Report].
LoadReport(client.GameID, uint, func(report.Report, error))
// PutReport stores given [report.Report] for a given [model.GameID] and turn number at the filesystem asynchronously.
// SaveReport stores given [report.Report] for a given [model.GameID] and turn number at the filesystem asynchronously.
// I/O or encoding error may occur, it that case callback func will be called with non-nil error.
PutReport(client.GameID, uint, report.Report, func(error))
SaveReport(client.GameID, uint, report.Report, func(error))
// LoadOrder loads a [order.Order] for a given [model.GameID] and turn number from filesystem asynchronously.
// Passed callback func will will accept non-nil error in case of I/O or decoding errors occuried,
// otherwise callback func accepts loaded [order.Order].
LoadOrder(client.GameID, uint, func(order.Order, error))
// PutOrder stores given [order.Order] for a given [model.GameID] and turn number at the filesystem asynchronously.
// SaveOrder stores given [order.Order] for a given [model.GameID] and turn number at the filesystem asynchronously.
// I/O or encoding error may occur, it that case callback func will be called with non-nil error.
PutOrder(client.GameID, uint, order.Order, func(error))
SaveOrder(client.GameID, uint, order.Order, func(error))
}