feat(inventory-go): spell name -> ID matchers for search spell filters
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
bd9c1ba556
commit
b0a6b494bf
2 changed files with 112 additions and 0 deletions
60
go-services/inventory-go/spell_filter.go
Normal file
60
go-services/inventory-go/spell_filter.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Spell-filter support for /search/items: implements the has_spell /
|
||||
// spell_contains / legendary_cantrips params the Go port had ignored
|
||||
// (design doc 2026-07-14; legacy inventory-service/main.py:3345-3452).
|
||||
// Names are matched against the in-memory enum-DB spell map; the resulting
|
||||
// IDs are filtered in SQL against the item_spells table.
|
||||
|
||||
// spellIDsContaining returns the IDs of all spells whose name contains q,
|
||||
// case-insensitively, in ascending order (deterministic SQL args).
|
||||
func spellIDsContaining(spells map[int]map[string]any, q string) []int {
|
||||
q = strings.ToLower(strings.TrimSpace(q))
|
||||
var ids []int
|
||||
for id, sp := range spells {
|
||||
name, _ := sp["name"].(string)
|
||||
if name != "" && strings.Contains(strings.ToLower(name), q) {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
sort.Ints(ids)
|
||||
return ids
|
||||
}
|
||||
|
||||
// spellIDsForCantrip matches one cantrip display name using the legacy
|
||||
// flexible rule: spell name contains the label OR the label contains the
|
||||
// spell name (both case-insensitive).
|
||||
func spellIDsForCantrip(spells map[int]map[string]any, cantrip string) []int {
|
||||
c := strings.ToLower(strings.TrimSpace(cantrip))
|
||||
var ids []int
|
||||
for id, sp := range spells {
|
||||
name, _ := sp["name"].(string)
|
||||
n := strings.ToLower(name)
|
||||
if n == "" {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(n, c) || strings.Contains(c, n) {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
sort.Ints(ids)
|
||||
return ids
|
||||
}
|
||||
|
||||
// exactSpellID returns the lowest ID whose name equals q case-insensitively.
|
||||
func exactSpellID(spells map[int]map[string]any, q string) (int, bool) {
|
||||
q = strings.ToLower(strings.TrimSpace(q))
|
||||
best := -1
|
||||
for id, sp := range spells {
|
||||
name, _ := sp["name"].(string)
|
||||
if strings.ToLower(name) == q && (best == -1 || id < best) {
|
||||
best = id
|
||||
}
|
||||
}
|
||||
return best, best != -1
|
||||
}
|
||||
52
go-services/inventory-go/spell_filter_test.go
Normal file
52
go-services/inventory-go/spell_filter_test.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Minimal stand-in for the enum-DB spell map (Server.spells).
|
||||
var testSpells = map[int]map[string]any{
|
||||
1: {"name": "Legendary Invulnerability"},
|
||||
2: {"name": "Epic Invulnerability"},
|
||||
3: {"name": "Legendary Summoning Prowess"},
|
||||
4: {"name": "Strength Other VI"},
|
||||
5: {"name": "Summoning"}, // shorter name CONTAINED IN a cantrip label
|
||||
6: {"name": ""}, // malformed entry must never match
|
||||
}
|
||||
|
||||
func TestSpellIDsContaining(t *testing.T) {
|
||||
if got := spellIDsContaining(testSpells, "invulnerability"); !reflect.DeepEqual(got, []int{1, 2}) {
|
||||
t.Errorf("substring match = %v, want [1 2]", got)
|
||||
}
|
||||
if got := spellIDsContaining(testSpells, "INVULN"); !reflect.DeepEqual(got, []int{1, 2}) {
|
||||
t.Errorf("case-insensitive match = %v, want [1 2]", got)
|
||||
}
|
||||
if got := spellIDsContaining(testSpells, "frostbite"); got != nil {
|
||||
t.Errorf("no-match = %v, want nil", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpellIDsForCantrip(t *testing.T) {
|
||||
// Forward direction: spell name contains the cantrip label.
|
||||
if got := spellIDsForCantrip(testSpells, "Invulnerability"); !reflect.DeepEqual(got, []int{1, 2}) {
|
||||
t.Errorf("forward contains = %v, want [1 2]", got)
|
||||
}
|
||||
// Both directions (legacy flexible rule): "Legendary Summoning Prowess"
|
||||
// matches spell 3 (equal) AND spell 5 ("Summoning" is contained in the label).
|
||||
if got := spellIDsForCantrip(testSpells, "Legendary Summoning Prowess"); !reflect.DeepEqual(got, []int{3, 5}) {
|
||||
t.Errorf("both-direction = %v, want [3 5]", got)
|
||||
}
|
||||
if got := spellIDsForCantrip(testSpells, "Piercing Bane"); got != nil {
|
||||
t.Errorf("no-match = %v, want nil", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExactSpellID(t *testing.T) {
|
||||
if id, ok := exactSpellID(testSpells, "legendary invulnerability"); !ok || id != 1 {
|
||||
t.Errorf("exact case-insensitive = (%d,%v), want (1,true)", id, ok)
|
||||
}
|
||||
if _, ok := exactSpellID(testSpells, "Invulnerability"); ok {
|
||||
t.Error("partial name must not exact-match")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue