diff --git a/client/storage/storage.go b/client/storage/storage.go deleted file mode 100644 index c52bd10..0000000 --- a/client/storage/storage.go +++ /dev/null @@ -1,26 +0,0 @@ -package storage - -import ( - "fmt" - "path/filepath" -) - -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) -} - -// 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 -} diff --git a/pkg/storage/fs/fs.go b/pkg/storage/fs/fs.go index eb16fbc..eda3467 100644 --- a/pkg/storage/fs/fs.go +++ b/pkg/storage/fs/fs.go @@ -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 } diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index ddf85a1..5f41e06 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -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)) }