Clarify and streamline D&D item events

This commit is contained in:
2026-07-25 23:59:56 +00:00
parent f7059607af
commit 927a7beb88
5 changed files with 31 additions and 899 deletions

View File

@@ -9,7 +9,6 @@ import (
"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/itemevents"
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared/diagnostics"
itemeventshape "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/validate/itemevents/shape"
)
@@ -43,19 +42,18 @@ func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationReq
return contracts.ValidationResult{Approved: true}, nil
}
index := source.NewDocumentIndex(req.Source)
var coverage *chunkCoverage
if req.Stage == string(pipeline.StageExtract) {
coverage = newChunkCoverage(req.Chunk)
}
issues := make([]string, 0)
for eventIndex, event := range req.Value.Events {
if itemevents.ValidSourceRefs(index, event.SourceRefs) {
if req.Stage != string(pipeline.StageExtract) || refsFitChunk(req.Source, req.Chunk, event.SourceRefs) {
continue
}
}
for refIndex, ref := range event.SourceRefs {
if err := index.ValidateRef(ref); err != nil {
issues = append(issues, fmt.Sprintf("events[%d].source_refs[%d]: %s", eventIndex, refIndex, diagnostics.Truncate(err.Error())))
continue
}
if req.Stage == string(pipeline.StageExtract) && !chunkContainsRef(req.Source, req.Chunk, ref) {
if coverage != nil && !coverage.contains(req.Source, ref) {
issues = append(issues, fmt.Sprintf("events[%d].source_refs[%d]: source reference is outside the current extraction chunk", eventIndex, refIndex))
}
}
@@ -66,17 +64,24 @@ func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationReq
return contracts.ValidationResult{Approved: false, ReasonCode: ReasonCode, Message: diagnostics.Aggregate("invalid item event source references", issues)}, nil
}
func refsFitChunk(doc *source.SourceDocument, chunk *source.Chunk, refs []source.SourceRef) bool {
for _, ref := range refs {
if !chunkContainsRef(doc, chunk, ref) {
return false
}
}
return true
type chunkCoverage struct {
sourceID string
unitIDs map[int]struct{}
}
func chunkContainsRef(doc *source.SourceDocument, chunk *source.Chunk, ref source.SourceRef) bool {
if chunk == nil || ref.SourceID != chunk.SourceID {
func newChunkCoverage(chunk *source.Chunk) *chunkCoverage {
coverage := &chunkCoverage{
sourceID: chunk.SourceID,
unitIDs: make(map[int]struct{}, len(chunk.Units)),
}
for _, unit := range chunk.Units {
coverage.unitIDs[unit.ID] = struct{}{}
}
return coverage
}
func (coverage *chunkCoverage) contains(doc *source.SourceDocument, ref source.SourceRef) bool {
if coverage == nil || doc == nil || ref.SourceID != coverage.sourceID {
return false
}
start, startOK := source.UnitIndex(doc, ref.StartUnitID)
@@ -84,12 +89,8 @@ func chunkContainsRef(doc *source.SourceDocument, chunk *source.Chunk, ref sourc
if !startOK || !endOK || start > end {
return false
}
units := make(map[int]struct{}, len(chunk.Units))
for _, unit := range chunk.Units {
units[unit.ID] = struct{}{}
}
for position := start; position <= end; position++ {
if _, found := units[doc.Units[position].ID]; !found {
if _, found := coverage.unitIDs[doc.Units[position].ID]; !found {
return false
}
}