Make explicit metadata queries lightweight
This commit is contained in:
@@ -19,10 +19,7 @@ type Metadata struct {
|
||||
// Lookup returns information for an effective path. Unknown and invalid paths
|
||||
// return false.
|
||||
func (m Metadata) Lookup(path Path) (FieldInfo, bool) {
|
||||
if !path.valid() || !matchesPathShape(path, m.effectivePaths[path.String()]) {
|
||||
return FieldInfo{}, false
|
||||
}
|
||||
info, ok := m.fields[path.String()]
|
||||
info, ok := m.effectiveField(path)
|
||||
if !ok {
|
||||
return FieldInfo{}, false
|
||||
}
|
||||
@@ -34,10 +31,23 @@ func (m Metadata) Lookup(path Path) (FieldInfo, bool) {
|
||||
// IsExplicit reports whether a surviving input contribution, including one
|
||||
// inherited from a sibling, explicitly defined path.
|
||||
func (m Metadata) IsExplicit(path Path) bool {
|
||||
info, ok := m.Lookup(path)
|
||||
info, ok := m.effectiveField(path)
|
||||
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
|
||||
// Path can be an ancestor when that container operation replaced or removed path.
|
||||
// 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
|
||||
// the query. Candidate paths are constructed from actual document containers.
|
||||
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
|
||||
}
|
||||
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) {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user