feat: use postgres
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
package gamestore
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"galaxy/lobby/internal/domain/game"
|
||||
)
|
||||
|
||||
// runtimeSnapshotJSON is the on-disk JSONB shape used for the denormalised
|
||||
// runtime snapshot column on `games`. Keys mirror the field names in
|
||||
// `game.RuntimeSnapshot` so a round-trip remains naked-comparable.
|
||||
type runtimeSnapshotJSON struct {
|
||||
CurrentTurn int `json:"current_turn"`
|
||||
RuntimeStatus string `json:"runtime_status,omitempty"`
|
||||
EngineHealthSummary string `json:"engine_health_summary,omitempty"`
|
||||
}
|
||||
|
||||
func marshalRuntimeSnapshot(snapshot game.RuntimeSnapshot) ([]byte, error) {
|
||||
payload := runtimeSnapshotJSON{
|
||||
CurrentTurn: snapshot.CurrentTurn,
|
||||
RuntimeStatus: snapshot.RuntimeStatus,
|
||||
EngineHealthSummary: snapshot.EngineHealthSummary,
|
||||
}
|
||||
encoded, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal runtime snapshot: %w", err)
|
||||
}
|
||||
return encoded, nil
|
||||
}
|
||||
|
||||
func unmarshalRuntimeSnapshot(payload []byte) (game.RuntimeSnapshot, error) {
|
||||
if len(payload) == 0 {
|
||||
return game.RuntimeSnapshot{}, nil
|
||||
}
|
||||
var stored runtimeSnapshotJSON
|
||||
if err := json.Unmarshal(payload, &stored); err != nil {
|
||||
return game.RuntimeSnapshot{}, fmt.Errorf("unmarshal runtime snapshot: %w", err)
|
||||
}
|
||||
return game.RuntimeSnapshot{
|
||||
CurrentTurn: stored.CurrentTurn,
|
||||
RuntimeStatus: stored.RuntimeStatus,
|
||||
EngineHealthSummary: stored.EngineHealthSummary,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// runtimeBindingJSON is the on-disk JSONB shape used for the optional
|
||||
// runtime binding column on `games`. The `bound_at_ms` field stores Unix
|
||||
// milliseconds so the JSON serialisation matches the previous Redis JSON
|
||||
// shape and the timezone is irrelevant inside the JSON payload itself; the
|
||||
// adapter still re-wraps the resulting time.Time with .UTC() before exposing
|
||||
// it to callers.
|
||||
type runtimeBindingJSON struct {
|
||||
ContainerID string `json:"container_id"`
|
||||
EngineEndpoint string `json:"engine_endpoint"`
|
||||
RuntimeJobID string `json:"runtime_job_id"`
|
||||
BoundAtMS int64 `json:"bound_at_ms"`
|
||||
}
|
||||
|
||||
// marshalRuntimeBinding returns nil bytes (SQL NULL) when binding is nil,
|
||||
// otherwise the JSON encoding of the binding.
|
||||
func marshalRuntimeBinding(binding *game.RuntimeBinding) ([]byte, error) {
|
||||
if binding == nil {
|
||||
return nil, nil
|
||||
}
|
||||
payload := runtimeBindingJSON{
|
||||
ContainerID: binding.ContainerID,
|
||||
EngineEndpoint: binding.EngineEndpoint,
|
||||
RuntimeJobID: binding.RuntimeJobID,
|
||||
BoundAtMS: binding.BoundAt.UTC().UnixMilli(),
|
||||
}
|
||||
encoded, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal runtime binding: %w", err)
|
||||
}
|
||||
return encoded, nil
|
||||
}
|
||||
|
||||
func unmarshalRuntimeBinding(payload []byte) (*game.RuntimeBinding, error) {
|
||||
if len(payload) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var stored runtimeBindingJSON
|
||||
if err := json.Unmarshal(payload, &stored); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal runtime binding: %w", err)
|
||||
}
|
||||
return &game.RuntimeBinding{
|
||||
ContainerID: stored.ContainerID,
|
||||
EngineEndpoint: stored.EngineEndpoint,
|
||||
RuntimeJobID: stored.RuntimeJobID,
|
||||
BoundAt: time.UnixMilli(stored.BoundAtMS).UTC(),
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user