Derive independent glossary context and protection
This commit is contained in:
39
internal/core/glossary/selection.go
Normal file
39
internal/core/glossary/selection.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// Package glossary derives consumer views from resolved configuration.
|
||||
package glossary
|
||||
|
||||
import "gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
|
||||
|
||||
// Entry is the known glossary content and its collection-scoped identity.
|
||||
type Entry struct {
|
||||
Collection string `json:"-"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Category string `json:"category"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Aliases []string `json:"aliases,omitempty"`
|
||||
Plural *string `json:"plural,omitempty"`
|
||||
}
|
||||
|
||||
type identity struct{ collection, id string }
|
||||
|
||||
// Select retains the first occurrence of each selected collection entry.
|
||||
func Select(collections map[string]config.GlossaryCollection, names []string) []Entry {
|
||||
seen := make(map[identity]struct{})
|
||||
var selected []Entry
|
||||
for _, name := range names {
|
||||
for _, item := range collections[name].Entries {
|
||||
key := identity{collection: name, id: item.ID}
|
||||
if _, exists := seen[key]; exists {
|
||||
continue
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
entry := Entry{Collection: name, ID: item.ID, Name: item.Name, Category: item.Category, Description: item.Description, Aliases: append([]string(nil), item.Aliases...)}
|
||||
if item.Plural != nil {
|
||||
plural := *item.Plural
|
||||
entry.Plural = &plural
|
||||
}
|
||||
selected = append(selected, entry)
|
||||
}
|
||||
}
|
||||
return selected
|
||||
}
|
||||
33
internal/core/glossary/selection_test.go
Normal file
33
internal/core/glossary/selection_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package glossary
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
|
||||
)
|
||||
|
||||
func TestSelectionRetainsDistinctIdentitiesAndDoesNotExposeExtensions(t *testing.T) {
|
||||
plural := "Shields"
|
||||
collections := map[string]config.GlossaryCollection{
|
||||
"spells": {Entries: []config.GlossaryEntry{{ID: "shield", Name: "Shield", Category: "spell", Aliases: []string{"Ward"}, Plural: &plural, Extra: map[string]any{"private": "do not forward"}}}},
|
||||
"items": {Entries: []config.GlossaryEntry{{ID: "shield", Name: "Shield", Category: "item"}}},
|
||||
}
|
||||
result := Select(collections, []string{"spells", "items", "spells"})
|
||||
if len(result) != 2 || result[0].Collection != "spells" || result[1].Collection != "items" {
|
||||
t.Fatalf("entries=%+v", result)
|
||||
}
|
||||
raw, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(string(raw), "do not forward") {
|
||||
t.Fatal("extension leaked into prompt projection")
|
||||
}
|
||||
result[0].Aliases[0] = "changed"
|
||||
*result[0].Plural = "changed"
|
||||
if collections["spells"].Entries[0].Aliases[0] != "Ward" || *collections["spells"].Entries[0].Plural != "Shields" {
|
||||
t.Fatal("projection changed configuration")
|
||||
}
|
||||
}
|
||||
71
internal/core/glossary/vocabulary.go
Normal file
71
internal/core/glossary/vocabulary.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package glossary
|
||||
|
||||
import "strings"
|
||||
|
||||
// Identity identifies an entry within its configured collection.
|
||||
type Identity struct {
|
||||
Collection string
|
||||
ID string
|
||||
}
|
||||
|
||||
// Term retains every entry that contributes a recognized spelling.
|
||||
type Term struct {
|
||||
Spelling string
|
||||
Identities []Identity
|
||||
}
|
||||
|
||||
// Vocabulary derives recognized spellings in selection order. Excluded entries
|
||||
// remove matching spellings regardless of which protected entries supplied them.
|
||||
func Vocabulary(entries, excluded []Entry) []Term {
|
||||
exclusions := make(map[string]struct{})
|
||||
for _, entry := range excluded {
|
||||
for _, spelling := range spellings(entry) {
|
||||
exclusions[strings.ToLower(spelling)] = struct{}{}
|
||||
}
|
||||
}
|
||||
indices := make(map[string]int)
|
||||
var terms []Term
|
||||
for _, entry := range entries {
|
||||
id := Identity{Collection: entry.Collection, ID: entry.ID}
|
||||
for _, spelling := range spellings(entry) {
|
||||
key := strings.ToLower(spelling)
|
||||
if _, excluded := exclusions[key]; excluded {
|
||||
continue
|
||||
}
|
||||
index, exists := indices[key]
|
||||
if !exists {
|
||||
index = len(terms)
|
||||
indices[key] = index
|
||||
terms = append(terms, Term{Spelling: spelling})
|
||||
}
|
||||
identities := terms[index].Identities
|
||||
found := false
|
||||
for _, existing := range identities {
|
||||
if existing == id {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
terms[index].Identities = append(identities, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
return terms
|
||||
}
|
||||
|
||||
func spellings(entry Entry) []string {
|
||||
var result []string
|
||||
for _, value := range append([]string{entry.Name}, entry.Aliases...) {
|
||||
value = strings.TrimSpace(value)
|
||||
if value != "" {
|
||||
result = append(result, value, value+"s")
|
||||
}
|
||||
}
|
||||
if entry.Plural != nil {
|
||||
if plural := strings.TrimSpace(*entry.Plural); plural != "" {
|
||||
result = append(result, plural)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
30
internal/core/glossary/vocabulary_test.go
Normal file
30
internal/core/glossary/vocabulary_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package glossary
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVocabularyKeepsContributorsAndExemptsOnlyActiveSpellings(t *testing.T) {
|
||||
plural := "Aegides"
|
||||
active := []Entry{{Collection: "spells", ID: "shield", Name: "Shield", Aliases: []string{"Ward"}}}
|
||||
protected := []Entry{
|
||||
active[0],
|
||||
{Collection: "items", ID: "shield", Name: "shield", Aliases: []string{"Aegis"}, Plural: &plural},
|
||||
}
|
||||
full := Vocabulary(protected, nil)
|
||||
if !reflect.DeepEqual(full[0].Identities, []Identity{{Collection: "spells", ID: "shield"}, {Collection: "items", ID: "shield"}}) {
|
||||
t.Fatalf("shared spelling contributors: %+v", full[0])
|
||||
}
|
||||
reduced := Vocabulary(protected, active)
|
||||
var got []string
|
||||
for _, term := range reduced {
|
||||
got = append(got, term.Spelling)
|
||||
}
|
||||
if !reflect.DeepEqual(got, []string{"Aegis", "Aegiss", "Aegides"}) {
|
||||
t.Fatalf("remaining spellings: %v", got)
|
||||
}
|
||||
if !reflect.DeepEqual(full, Vocabulary(protected, nil)) || protected[0].Aliases[0] != "Ward" {
|
||||
t.Fatal("deriving an exemption changed another view or its inputs")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user