refactor: storage interface
This commit is contained in:
+57
-12
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user