Share indexed fallback locations during effective projection
This commit is contained in:
@@ -887,7 +887,14 @@ do not replace the agreed scheduling algorithm or add global caches.
|
|||||||
|
|
||||||
## 22. Index Fallback Location Lookup During Effective Projection — P2
|
## 22. Index Fallback Location Lookup During Effective Projection — P2
|
||||||
|
|
||||||
Status: [ ] Open.
|
Status: [x] Complete.
|
||||||
|
|
||||||
|
Completion: effective location projection lazily builds one read-only fallback
|
||||||
|
document lookup and shares it across field and entry visits. The ordinary
|
||||||
|
nearest-location helper retains its existing behavior. Added broad mixed
|
||||||
|
authored/default mappings, ancestor fallback, literal key/index mismatch,
|
||||||
|
synthetic CLI coordinates, and detached-projection coverage. Existing secret
|
||||||
|
and validation diagnostics and `./scripts/check.sh` pass.
|
||||||
|
|
||||||
Owner: `documentWithMetadataLocations` in
|
Owner: `documentWithMetadataLocations` in
|
||||||
[`override_context.go`](../../internal/engine/override_context.go), using
|
[`override_context.go`](../../internal/engine/override_context.go), using
|
||||||
|
|||||||
79
internal/engine/metadata_locations_test.go
Normal file
79
internal/engine/metadata_locations_test.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMetadataLocationsAcrossBroadAuthoredAndDefaultMappings(t *testing.T) {
|
||||||
|
type config map[string]map[string]string
|
||||||
|
defaults := config{}
|
||||||
|
var body strings.Builder
|
||||||
|
for index := range 96 {
|
||||||
|
name := fmt.Sprintf("authored-%03d", index)
|
||||||
|
fmt.Fprintf(&body, "%s: {present: source}\n", name)
|
||||||
|
defaults[name] = map[string]string{"absent": "default"}
|
||||||
|
defaults[fmt.Sprintf("default-%03d", index)] = map[string]string{"absent": "default"}
|
||||||
|
}
|
||||||
|
input := parseDocumentForDecode(t, body.String())
|
||||||
|
decoded, issues := decodeConfiguration(mustSchemaFor[config](t), reflect.ValueOf(defaults), input)
|
||||||
|
if len(issues) != 0 {
|
||||||
|
t.Fatal(issues)
|
||||||
|
}
|
||||||
|
located := documentWithMetadataLocations(decoded.final, metadataFromDecoded(decoded), input)
|
||||||
|
inputIndex := buildDocumentLookup(input)
|
||||||
|
locatedIndex := buildDocumentLookup(located)
|
||||||
|
for index := range 96 {
|
||||||
|
authored := Keys(fmt.Sprintf("authored-%03d", index))
|
||||||
|
mapping, _ := documentAtPathWithLookup(input, authored, inputIndex)
|
||||||
|
present, _ := documentAtPathWithLookup(input, authored.Key("present"), inputIndex)
|
||||||
|
for _, test := range []struct {
|
||||||
|
path Path
|
||||||
|
want document.Location
|
||||||
|
}{
|
||||||
|
{authored.Key("present"), present.Location},
|
||||||
|
{authored.Key("absent"), mapping.Location},
|
||||||
|
{Keys(fmt.Sprintf("default-%03d", index), "absent"), input.Location},
|
||||||
|
} {
|
||||||
|
node, found := documentAtPathWithLookup(located, test.path, locatedIndex)
|
||||||
|
if !found || node.Location != test.want {
|
||||||
|
t.Fatalf("location at %s = %#v, want %#v", test.path, node, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
walkDocument(located, Path{}, func(_ Path, node *document.Node) {
|
||||||
|
for _, entry := range node.Mapping {
|
||||||
|
if entry.KeyLocation != entry.Value.Location || entry.ValueLocation != entry.Value.Location {
|
||||||
|
t.Fatal("entry and value locations disagree")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
before := decoded.final.Mapping[0].Value.Mapping[0].Value.Text
|
||||||
|
located.Mapping[0].Value.Mapping[0].Value.Text = "changed snapshot"
|
||||||
|
if decoded.final.Mapping[0].Value.Mapping[0].Value.Text != before || input.Mapping[0].Value.Mapping[0].Value.Text != "source" {
|
||||||
|
t.Fatal("location projection changed a borrowed input")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIndexedNearestLocationsPreserveMissingAndSyntheticCoordinates(t *testing.T) {
|
||||||
|
root := parseDocumentForDecode(t, "mapping: {'0': value}\nitems: [{value: source}]\nscalar: stop\n")
|
||||||
|
lookup := buildDocumentLookup(root)
|
||||||
|
for _, path := range []Path{Path{}, Keys("missing"), Keys("mapping", "missing"), Keys("mapping", "0"), Keys("mapping").Index(0), Keys("items").Index(4), Keys("items").Index(0).Key("missing"), Keys("scalar", "child"), Path{}.Index(-1)} {
|
||||||
|
want, wantOK := nearestDocumentLocation(root, path)
|
||||||
|
got, gotOK := nearestDocumentLocationWithLookup(root, path, lookup)
|
||||||
|
if got != want || gotOK != wantOK {
|
||||||
|
t.Fatalf("nearest location changed for %s: %#v/%t, want %#v/%t", path, got, gotOK, want, wantOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final := parseDocumentForDecode(t, "value: cli\n")
|
||||||
|
synthetic := Location{SourceID: "cli-1"}
|
||||||
|
metadata := Metadata{fields: map[string]FieldInfo{"/value": {Origin: Origin{Layer: LayerCLI, Locations: []Location{synthetic}}}}}
|
||||||
|
located := documentWithMetadataLocations(final, metadata, root)
|
||||||
|
if location := located.Mapping[0].Value.Location; location.SourceID != "cli-1" || location.Line != 0 || location.Column != 0 {
|
||||||
|
t.Fatalf("synthetic CLI location was replaced by fallback: %#v", location)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -171,16 +171,23 @@ func documentWithMetadataLocations(root *document.Node, metadata Metadata, fallb
|
|||||||
if len(fallback) != 0 {
|
if len(fallback) != 0 {
|
||||||
fallbackRoot = fallback[0]
|
fallbackRoot = fallback[0]
|
||||||
}
|
}
|
||||||
|
var lookup *documentLookup
|
||||||
|
fallbackLocation := func(path Path) (document.Location, bool) {
|
||||||
|
if lookup == nil {
|
||||||
|
lookup = buildDocumentLookup(fallbackRoot)
|
||||||
|
}
|
||||||
|
return nearestDocumentLocationWithLookup(fallbackRoot, path, lookup)
|
||||||
|
}
|
||||||
var visit func(*document.Node, Path)
|
var visit func(*document.Node, Path)
|
||||||
visit = func(node *document.Node, path Path) {
|
visit = func(node *document.Node, path Path) {
|
||||||
pointer := path.String()
|
pointer := path.String()
|
||||||
if info, exists := metadata.fields[pointer]; exists {
|
if info, exists := metadata.fields[pointer]; exists {
|
||||||
if location, ok := metadataFieldLocation(metadata, pointer, info.Origin); ok {
|
if location, ok := metadataFieldLocation(metadata, pointer, info.Origin); ok {
|
||||||
node.Location = location
|
node.Location = location
|
||||||
} else if location, ok := nearestDocumentLocation(fallbackRoot, path); ok {
|
} else if location, ok := fallbackLocation(path); ok {
|
||||||
node.Location = location
|
node.Location = location
|
||||||
}
|
}
|
||||||
} else if location, ok := nearestDocumentLocation(fallbackRoot, path); ok {
|
} else if location, ok := fallbackLocation(path); ok {
|
||||||
node.Location = location
|
node.Location = location
|
||||||
}
|
}
|
||||||
switch node.Kind {
|
switch node.Kind {
|
||||||
@@ -192,11 +199,11 @@ func documentWithMetadataLocations(root *document.Node, metadata Metadata, fallb
|
|||||||
if location, ok := metadataFieldLocation(metadata, childPath.String(), info.Origin); ok {
|
if location, ok := metadataFieldLocation(metadata, childPath.String(), info.Origin); ok {
|
||||||
entry.KeyLocation = location
|
entry.KeyLocation = location
|
||||||
entry.ValueLocation = location
|
entry.ValueLocation = location
|
||||||
} else if location, ok := nearestDocumentLocation(fallbackRoot, childPath); ok {
|
} else if location, ok := fallbackLocation(childPath); ok {
|
||||||
entry.KeyLocation = location
|
entry.KeyLocation = location
|
||||||
entry.ValueLocation = location
|
entry.ValueLocation = location
|
||||||
}
|
}
|
||||||
} else if location, ok := nearestDocumentLocation(fallbackRoot, childPath); ok {
|
} else if location, ok := fallbackLocation(childPath); ok {
|
||||||
entry.KeyLocation = location
|
entry.KeyLocation = location
|
||||||
entry.ValueLocation = location
|
entry.ValueLocation = location
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,10 @@ func validationMutationDiagnostic(path Path) Diagnostic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func nearestDocumentLocation(root *document.Node, path Path) (document.Location, bool) {
|
func nearestDocumentLocation(root *document.Node, path Path) (document.Location, bool) {
|
||||||
|
return nearestDocumentLocationWithLookup(root, path, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func nearestDocumentLocationWithLookup(root *document.Node, path Path, lookup *documentLookup) (document.Location, bool) {
|
||||||
if root == nil || !path.valid() {
|
if root == nil || !path.valid() {
|
||||||
return document.Location{}, false
|
return document.Location{}, false
|
||||||
}
|
}
|
||||||
@@ -135,18 +139,22 @@ func nearestDocumentLocation(root *document.Node, path Path) (document.Location,
|
|||||||
if segment.kind == pathSegmentIndex {
|
if segment.kind == pathSegmentIndex {
|
||||||
return nearest, locationKnown(nearest)
|
return nearest, locationKnown(nearest)
|
||||||
}
|
}
|
||||||
found := false
|
position := -1
|
||||||
for _, entry := range current.Mapping {
|
if lookup != nil {
|
||||||
if entry.Key == segment.key {
|
position = lookup.find(current, segment.key)
|
||||||
current = entry.Value
|
} else {
|
||||||
nearest = current.Location
|
for index, entry := range current.Mapping {
|
||||||
found = true
|
if entry.Key == segment.key {
|
||||||
break
|
position = index
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if position < 0 {
|
||||||
return nearest, locationKnown(nearest)
|
return nearest, locationKnown(nearest)
|
||||||
}
|
}
|
||||||
|
current = current.Mapping[position].Value
|
||||||
|
nearest = current.Location
|
||||||
case document.Sequence:
|
case document.Sequence:
|
||||||
index, ok := schemaIndex(segment)
|
index, ok := schemaIndex(segment)
|
||||||
if !ok || index >= len(current.Sequence) {
|
if !ok || index >= len(current.Sequence) {
|
||||||
|
|||||||
Reference in New Issue
Block a user