95 lines
3.1 KiB
Go
95 lines
3.1 KiB
Go
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
|
|
}
|