refactor: remove persister abstraction layer
Some checks failed
CI / Deploy / test (pull_request) Successful in 8s
CI / Deploy / lint (pull_request) Failing after 46s
CI / Deploy / deploy (pull_request) Has been skipped

Inline persistence logic directly into game stores and handlers:
- game/persist.go: DB mapping methods on GameStore and GameInstance
- snake/persist.go: DB mapping methods on SnakeStore and SnakeGameInstance
- Chat persistence inlined into c4game handlers
- Delete db/persister.go (GamePersister, SnakePersister, ChatPersister)
- Stores now take *repository.Queries directly instead of Persister interface
This commit is contained in:
Ryan Hamamura
2026-03-02 12:30:33 -10:00
parent 8c3b3fc6ea
commit 2aa026b1d5
10 changed files with 475 additions and 448 deletions

157
game/persist.go Normal file
View File

@@ -0,0 +1,157 @@
package game
import (
"context"
"database/sql"
"github.com/ryanhamamura/c4/db/repository"
)
// Persistence methods on GameStore (used during Get to hydrate from DB).
func (gs *GameStore) saveGame(g *Game) error {
ctx := context.Background()
_, err := gs.queries.GetGame(ctx, g.ID)
if err == sql.ErrNoRows {
_, err = gs.queries.CreateGame(ctx, repository.CreateGameParams{
ID: g.ID,
Board: g.BoardToJSON(),
CurrentTurn: int64(g.CurrentTurn),
Status: int64(g.Status),
})
return err
}
if err != nil {
return err
}
return gs.queries.UpdateGame(ctx, updateGameParams(g))
}
func (gs *GameStore) loadGame(id string) (*Game, error) {
row, err := gs.queries.GetGame(context.Background(), id)
if err != nil {
return nil, err
}
return gameFromRow(row)
}
func (gs *GameStore) loadGamePlayers(id string) ([]*Player, error) {
rows, err := gs.queries.GetGamePlayers(context.Background(), id)
if err != nil {
return nil, err
}
return playersFromRows(rows), nil
}
// Persistence methods on GameInstance (used during gameplay mutations).
func (gi *GameInstance) saveGame(g *Game) error {
ctx := context.Background()
_, err := gi.queries.GetGame(ctx, g.ID)
if err == sql.ErrNoRows {
_, err = gi.queries.CreateGame(ctx, repository.CreateGameParams{
ID: g.ID,
Board: g.BoardToJSON(),
CurrentTurn: int64(g.CurrentTurn),
Status: int64(g.Status),
})
return err
}
if err != nil {
return err
}
return gi.queries.UpdateGame(ctx, updateGameParams(g))
}
func (gi *GameInstance) saveGamePlayer(gameID string, player *Player, slot int) error {
var userID, guestPlayerID sql.NullString
if player.UserID != nil {
userID = sql.NullString{String: *player.UserID, Valid: true}
} else {
guestPlayerID = sql.NullString{String: string(player.ID), Valid: true}
}
return gi.queries.CreateGamePlayer(context.Background(), repository.CreateGamePlayerParams{
GameID: gameID,
UserID: userID,
GuestPlayerID: guestPlayerID,
Nickname: player.Nickname,
Color: int64(player.Color),
Slot: int64(slot),
})
}
// Shared helpers for domain ↔ DB mapping.
func updateGameParams(g *Game) repository.UpdateGameParams {
var winnerUserID sql.NullString
if g.Winner != nil && g.Winner.UserID != nil {
winnerUserID = sql.NullString{String: *g.Winner.UserID, Valid: true}
}
var winningCells sql.NullString
if wc := g.WinningCellsToJSON(); wc != "" {
winningCells = sql.NullString{String: wc, Valid: true}
}
var rematchGameID sql.NullString
if g.RematchGameID != nil {
rematchGameID = sql.NullString{String: *g.RematchGameID, Valid: true}
}
return repository.UpdateGameParams{
Board: g.BoardToJSON(),
CurrentTurn: int64(g.CurrentTurn),
Status: int64(g.Status),
WinnerUserID: winnerUserID,
WinningCells: winningCells,
RematchGameID: rematchGameID,
ID: g.ID,
}
}
func gameFromRow(row repository.Game) (*Game, error) {
g := &Game{
ID: row.ID,
CurrentTurn: int(row.CurrentTurn),
Status: GameStatus(row.Status),
}
if err := g.BoardFromJSON(row.Board); err != nil {
return nil, err
}
if row.WinningCells.Valid {
g.WinningCellsFromJSON(row.WinningCells.String)
}
if row.RematchGameID.Valid {
g.RematchGameID = &row.RematchGameID.String
}
return g, nil
}
func playersFromRows(rows []repository.GamePlayer) []*Player {
players := make([]*Player, 0, len(rows))
for _, row := range rows {
player := &Player{
Nickname: row.Nickname,
Color: int(row.Color),
}
if row.UserID.Valid {
player.UserID = &row.UserID.String
player.ID = PlayerID(row.UserID.String)
} else if row.GuestPlayerID.Valid {
player.ID = PlayerID(row.GuestPlayerID.String)
}
players = append(players, player)
}
return players
}