package entities import ( "strings" "sync/atomic" "gitea.nik4nao.com/nik/home-services/alexa-bridge/internal/core/domain" ) // Resolver implements driven.EntityResolver via an atomically-swapped // snapshot map, so an in-flight Alexa request never observes a half-built // map while a refresh is in progress. type Resolver struct { snapshot atomic.Pointer[map[string]domain.Entity] } // NewResolver constructs a Resolver with an empty snapshot; call Replace // (directly, or via Refresher.Start) to populate it before serving requests. func NewResolver() *Resolver { r := &Resolver{} empty := map[string]domain.Entity{} r.snapshot.Store(&empty) return r } // Replace rebuilds the lookup map, keyed by normalized friendly name, and // swaps it in atomically. func (r *Resolver) Replace(list []domain.Entity) { m := make(map[string]domain.Entity, len(list)) for _, e := range list { m[normalize(e.FriendlyName)] = e } r.snapshot.Store(&m) } // Resolve implements driven.EntityResolver. Matching is exact-match only on // the normalized (lowercased, trimmed) friendly name — see // alexa-bridge/plan.md's Decisions on open questions #5 for why fuzzy // matching isn't implemented here. func (r *Resolver) Resolve(friendlyName string) (domain.Entity, bool) { m := *r.snapshot.Load() e, ok := m[normalize(friendlyName)] return e, ok } func normalize(s string) string { return strings.ToLower(strings.TrimSpace(s)) }