Share D&D cited source traversal

This commit is contained in:
2026-07-21 14:37:12 +00:00
parent 732b13669f
commit 07460341e3
11 changed files with 322 additions and 129 deletions

View File

@@ -5,10 +5,10 @@ import (
"fmt"
"strings"
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
spellshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/spells/shape"
)
@@ -29,46 +29,28 @@ func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationReq
if err := spellshape.Validate(req.Value); err != nil {
return contracts.ValidationResult{Approved: true}, nil
}
citedTexts := make([]string, len(req.Value.SpellCasts))
for spellIndex, spell := range req.Value.SpellCasts {
citedText, err := shared.CitedText(req.Source, spell.SourceRefs)
if err != nil {
return contracts.ValidationResult{Approved: true}, nil
}
citedTexts[spellIndex] = citedText
}
var warnings []contracts.Warning
for spellIndex, spell := range req.Value.SpellCasts {
if !spellAppearsInCitedText(req.Source, spell) {
if !spellAppearsInCitedText(citedTexts[spellIndex], spell) {
warnings = append(warnings, contracts.Warning{Scope: fmt.Sprintf("spell_casts[%d]", spellIndex), ReasonCode: WarningReasonCode, Message: fmt.Sprintf("spell %q was not found in cited source text", strings.TrimSpace(spell.Spell))})
}
}
return contracts.ValidationResult{Approved: true, Warnings: warnings}, nil
}
func spellAppearsInCitedText(doc *source.SourceDocument, spell dnd.SpellCast) bool {
name := strings.ToLower(strings.TrimSpace(spell.Spell))
func spellAppearsInCitedText(citedText string, spell dnd.SpellCast) bool {
name := strings.TrimSpace(spell.Spell)
if name == "" {
return true
}
for _, ref := range spell.SourceRefs {
if text, ok := citedText(doc, ref); ok && strings.Contains(strings.ToLower(text), name) {
return true
}
}
return false
}
func citedText(doc *source.SourceDocument, ref source.SourceRef) (string, bool) {
if doc == nil {
return "", false
}
start, ok := source.UnitIndex(doc, ref.StartUnitID)
if !ok {
return "", false
}
end, ok := source.UnitIndex(doc, ref.EndUnitID)
if !ok || start > end {
return "", false
}
var b strings.Builder
for i := start; i <= end; i++ {
if b.Len() > 0 {
b.WriteByte('\n')
}
b.WriteString(doc.Units[i].Text)
}
return b.String(), true
return shared.ContainsTokenSequence(citedText, name)
}
func Spec() pipeline.ValidatorSpec {
return pipeline.ValidatorSpec{Key: Key, ExecutionClass: contracts.ExecutionClassDeterministic}