// Package engineimage resolves the Docker reference Lobby publishes on // `runtime:start_jobs`. The reference is built from a configurable // template that must contain the literal `{engine_version}` placeholder // and a per-game `target_engine_version`. // // The resolver intentionally performs only template substitution and a // non-empty-version guard. Semver validation of the engine version // itself lives in `lobby/internal/domain/game` and runs at game-record // construction time; by the time `startgame.Service.Handle` reads the // record the version is already validated. package engineimage import ( "errors" "fmt" "strings" ) // VersionPlaceholder is the literal token a template must contain. The // resolver substitutes it with the per-game engine version verbatim. const VersionPlaceholder = "{engine_version}" // Resolver substitutes a per-game engine version into a pre-validated // template. The template is validated once at construction so per-game // `Resolve` calls remain pure string substitution. type Resolver struct { template string } // NewResolver returns a Resolver that uses template for every Resolve // call. It returns an error if template is empty or does not contain // VersionPlaceholder. func NewResolver(template string) (*Resolver, error) { trimmed := strings.TrimSpace(template) if trimmed == "" { return nil, errors.New("engine image resolver: template must not be empty") } if !strings.Contains(trimmed, VersionPlaceholder) { return nil, fmt.Errorf( "engine image resolver: template %q must contain placeholder %q", template, VersionPlaceholder, ) } return &Resolver{template: trimmed}, nil } // Template returns the validated template string the resolver was // constructed with. The accessor is intended for diagnostics and tests. func (resolver *Resolver) Template() string { if resolver == nil { return "" } return resolver.template } // Resolve substitutes VersionPlaceholder in the validated template with // version. It returns an error when version is empty or whitespace. func (resolver *Resolver) Resolve(version string) (string, error) { if resolver == nil { return "", errors.New("engine image resolver: nil resolver") } if strings.TrimSpace(version) == "" { return "", errors.New("engine image resolver: engine version must not be empty") } return strings.ReplaceAll(resolver.template, VersionPlaceholder, version), nil }