Make explicit metadata queries lightweight
This commit is contained in:
@@ -103,7 +103,7 @@ not new public guarantees. A documentation-only stage needs documentation checks
|
|||||||
deferred to the first stage that consumes each algorithm so they can remain
|
deferred to the first stage that consumes each algorithm so they can remain
|
||||||
small and bounded.
|
small and bounded.
|
||||||
|
|
||||||
## Stage 2: Make presence-only queries lightweight
|
## Stage 2: Make presence-only queries lightweight — Complete
|
||||||
|
|
||||||
Roadmap candidate 1. Owner: `internal/engine/metadata.go`.
|
Roadmap candidate 1. Owner: `internal/engine/metadata.go`.
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,7 @@ type Metadata struct {
|
|||||||
// Lookup returns information for an effective path. Unknown and invalid paths
|
// Lookup returns information for an effective path. Unknown and invalid paths
|
||||||
// return false.
|
// return false.
|
||||||
func (m Metadata) Lookup(path Path) (FieldInfo, bool) {
|
func (m Metadata) Lookup(path Path) (FieldInfo, bool) {
|
||||||
if !path.valid() || !matchesPathShape(path, m.effectivePaths[path.String()]) {
|
info, ok := m.effectiveField(path)
|
||||||
return FieldInfo{}, false
|
|
||||||
}
|
|
||||||
info, ok := m.fields[path.String()]
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return FieldInfo{}, false
|
return FieldInfo{}, false
|
||||||
}
|
}
|
||||||
@@ -34,10 +31,23 @@ func (m Metadata) Lookup(path Path) (FieldInfo, bool) {
|
|||||||
// IsExplicit reports whether a surviving input contribution, including one
|
// IsExplicit reports whether a surviving input contribution, including one
|
||||||
// inherited from a sibling, explicitly defined path.
|
// inherited from a sibling, explicitly defined path.
|
||||||
func (m Metadata) IsExplicit(path Path) bool {
|
func (m Metadata) IsExplicit(path Path) bool {
|
||||||
info, ok := m.Lookup(path)
|
info, ok := m.effectiveField(path)
|
||||||
return ok && info.Explicit
|
return ok && info.Explicit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m Metadata) effectiveField(path Path) (FieldInfo, bool) {
|
||||||
|
if !path.valid() {
|
||||||
|
return FieldInfo{}, false
|
||||||
|
}
|
||||||
|
pointer := path.String()
|
||||||
|
candidate, ok := m.effectivePaths[pointer]
|
||||||
|
if !ok || !matchesPathShapeWithPointer(path, candidate, pointer) {
|
||||||
|
return FieldInfo{}, false
|
||||||
|
}
|
||||||
|
info, ok := m.fields[pointer]
|
||||||
|
return info, ok
|
||||||
|
}
|
||||||
|
|
||||||
// History returns detached definition, inheritance, and resolution records for path. A record's
|
// History returns detached definition, inheritance, and resolution records for path. A record's
|
||||||
// Path can be an ancestor when that container operation replaced or removed path.
|
// Path can be an ancestor when that container operation replaced or removed path.
|
||||||
// Removed historical paths can have history even though Lookup returns false.
|
// Removed historical paths can have history even though Lookup returns false.
|
||||||
@@ -79,7 +89,14 @@ func (m Metadata) HistoricalPaths() []Path {
|
|||||||
// Textual pointers are lookup keys, but explicit key/index kinds remain part of
|
// Textual pointers are lookup keys, but explicit key/index kinds remain part of
|
||||||
// the query. Candidate paths are constructed from actual document containers.
|
// the query. Candidate paths are constructed from actual document containers.
|
||||||
func matchesPathShape(query, candidate Path) bool {
|
func matchesPathShape(query, candidate Path) bool {
|
||||||
if !query.valid() || query.String() != candidate.String() || len(query.segments) != len(candidate.segments) {
|
if !query.valid() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return matchesPathShapeWithPointer(query, candidate, query.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchesPathShapeWithPointer(query, candidate Path, pointer string) bool {
|
||||||
|
if pointer != candidate.String() || len(query.segments) != len(candidate.segments) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i, segment := range query.segments {
|
for i, segment := range query.segments {
|
||||||
|
|||||||
@@ -285,6 +285,75 @@ func TestResolutionInputKeepsDetachedEntryMetadata(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMetadataIsExplicitAgreesWithLookup(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type entry struct {
|
||||||
|
Kept string `yaml:"kept"`
|
||||||
|
Set string `yaml:"set"`
|
||||||
|
Enabled bool `yaml:"enabled"`
|
||||||
|
Count int `yaml:"count"`
|
||||||
|
}
|
||||||
|
type config struct {
|
||||||
|
Nested entry `yaml:"nested"`
|
||||||
|
Nullable *string `yaml:"nullable"`
|
||||||
|
Nodes map[string]entry `yaml:"nodes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
input := []byte(`nested: {set: configured, enabled: false, count: 0}
|
||||||
|
nullable: null
|
||||||
|
nodes:
|
||||||
|
base: {enabled: false, count: 0}
|
||||||
|
copy: {extends: base}
|
||||||
|
`)
|
||||||
|
result, err := testLoad(Bytes("metadata-presence.yml", input), Options[config]{Definition: Definition[config]{
|
||||||
|
Defaults: func() config {
|
||||||
|
return config{
|
||||||
|
Nested: entry{Kept: "default", Set: "default", Enabled: true, Count: 7},
|
||||||
|
Nodes: map[string]entry{"copy": {Kept: "node default"}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
metadata := result.Metadata()
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
path Path
|
||||||
|
wantExplicit bool
|
||||||
|
wantNull bool
|
||||||
|
wantInherited bool
|
||||||
|
}{
|
||||||
|
{name: "default", path: Keys("nested", "kept")},
|
||||||
|
{name: "direct input", path: Keys("nested", "set"), wantExplicit: true},
|
||||||
|
{name: "explicit false", path: Keys("nested", "enabled"), wantExplicit: true},
|
||||||
|
{name: "explicit zero", path: Keys("nested", "count"), wantExplicit: true},
|
||||||
|
{name: "explicit null", path: Keys("nullable"), wantExplicit: true, wantNull: true},
|
||||||
|
{name: "inherited input", path: Keys("nodes", "copy", "enabled"), wantExplicit: true, wantInherited: true},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
info, found := metadata.Lookup(test.path)
|
||||||
|
if !found {
|
||||||
|
t.Fatalf("Lookup(%s) did not find an effective path", test.path)
|
||||||
|
}
|
||||||
|
if got := metadata.IsExplicit(test.path); got != info.Explicit || got != test.wantExplicit {
|
||||||
|
t.Fatalf("IsExplicit(%s) = %v, Lookup explicit = %v, want %v", test.path, got, info.Explicit, test.wantExplicit)
|
||||||
|
}
|
||||||
|
if info.Null != test.wantNull {
|
||||||
|
t.Fatalf("Lookup(%s).Null = %v, want %v", test.path, info.Null, test.wantNull)
|
||||||
|
}
|
||||||
|
if inherited := len(info.Origin.Inheritance) != 0; inherited != test.wantInherited {
|
||||||
|
t.Fatalf("Lookup(%s) inherited = %v, want %v", test.path, inherited, test.wantInherited)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if result.Config.Nested.Enabled || result.Config.Nested.Count != 0 || result.Config.Nullable != nil {
|
||||||
|
t.Fatalf("explicit values changed during loading: %#v", result.Config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestQueriesRespectActualHistoricalAndEffectiveContainers(t *testing.T) {
|
func TestQueriesRespectActualHistoricalAndEffectiveContainers(t *testing.T) {
|
||||||
result, err := testLoad[map[string]any](Bytes("base", []byte("items: [1]\nm: {'0': 2}\nx: 3\nretired: {child: 4}\n")), Options[map[string]any]{Overrides: []Override{{Path: Keys("retired"), Value: "removed", Flag: "--retired"}}})
|
result, err := testLoad[map[string]any](Bytes("base", []byte("items: [1]\nm: {'0': 2}\nx: 3\nretired: {child: 4}\n")), Options[map[string]any]{Overrides: []Override{{Path: Keys("retired"), Value: "removed", Flag: "--retired"}}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user