Files
configkit/internal/engine/load_metadata.go

221 lines
6.8 KiB
Go

package engine
import (
"slices"
"go.maximumdirect.net/configkit/internal/document"
)
// resolutionInput is the immutable entry-state provenance available to a
// resolution callback. It owns detached metadata and source records so later
// resolution updates cannot change the entry view.
type resolutionInput struct {
metadata Metadata
sources map[string]SourceInfo
contexts map[string]ResourceContext
}
func newResolutionInput(metadata Metadata, final *document.Node, sources []SourceInfo, contexts ...map[string]ResourceContext) resolutionInput {
frozen := Metadata{
fields: make(map[string]FieldInfo, len(metadata.fields)),
effectivePaths: effectivePathIndex(final),
}
for pointer, info := range metadata.fields {
frozen.fields[pointer] = cloneFieldInfo(info)
}
indexed := make(map[string]SourceInfo, len(sources))
for _, source := range sources {
if source.ID == "" {
panic("configkit: source identity is missing")
}
if _, exists := indexed[source.ID]; exists {
panic("configkit: source identity is duplicated")
}
indexed[source.ID] = cloneSourceInfo(source)
}
result := resolutionInput{metadata: frozen, sources: indexed}
if len(contexts) != 0 {
result.contexts = cloneResourceContexts(contexts[0])
}
return result
}
func (input resolutionInput) lookup(path Path) (FieldInfo, bool) {
return input.metadata.Lookup(path)
}
func (input resolutionInput) source(id string) (SourceInfo, bool) {
source, ok := input.sources[id]
if !ok {
return SourceInfo{}, false
}
return cloneSourceInfo(source), true
}
func (input resolutionInput) sourceContext(id string) (ResourceContext, bool) {
context, ok := input.contexts[id]
return context, ok
}
func metadataFromDecoded(decoded decodedConfiguration) Metadata {
metadata := Metadata{
fields: make(map[string]FieldInfo),
historyBuilder: newHistoryBuilder(),
}
metadata.historyBuilder.observeBaseline(decoded.baseline)
collectFieldInfo(decoded.final, Path{}, decoded, metadata.fields)
return metadata
}
func originContainedBy(candidate, container Origin) bool {
if candidate.Layer != container.Layer || candidate.Operation != container.Operation {
return false
}
availableLocations := make(map[Location]struct{}, len(container.Locations))
for _, location := range container.Locations {
availableLocations[location] = struct{}{}
}
for _, location := range candidate.Locations {
if _, found := availableLocations[location]; !found {
return false
}
}
if (len(candidate.contributors) == 0) != (len(container.contributors) == 0) {
return false
}
for _, contributor := range candidate.contributors {
found := false
for _, available := range container.contributors {
if contributor.location == available.location &&
slices.Equal(contributor.source.segments, available.source.segments) &&
sameInheritanceTrace(contributor.trace, available.trace) {
found = true
break
}
}
if !found {
return false
}
}
return true
}
func collectFieldInfo(node *document.Node, path Path, decoded decodedConfiguration, fields map[string]FieldInfo) {
pointer := path.String()
_, explicit := decoded.presence[pointer]
fields[pointer] = FieldInfo{
Explicit: explicit,
Null: node.Kind == document.Null,
Origin: cloneOrigin(decoded.origins[pointer]),
}
switch node.Kind {
case document.Mapping:
for _, entry := range node.Mapping {
collectFieldInfo(entry.Value, path.Key(entry.Key), decoded, fields)
}
case document.Sequence:
for index, child := range node.Sequence {
collectFieldInfo(child, path.Index(index), decoded, fields)
}
}
}
func cloneMetadata(metadata Metadata) Metadata {
if metadata.fields == nil && metadata.historyModel == nil {
return Metadata{}
}
cloned := Metadata{
fields: make(map[string]FieldInfo, len(metadata.fields)),
historyModel: metadata.historyModel,
}
cloned.effectivePaths = make(map[string]Path, len(metadata.effectivePaths))
for pointer, path := range metadata.effectivePaths {
cloned.effectivePaths[pointer] = path.clone()
}
for path, info := range metadata.fields {
cloned.fields[path] = cloneFieldInfo(info)
}
return cloned
}
func applyResolutionMetadata(metadata *Metadata, resolved resolutionResult, entry resolutionInput) {
finalPaths := make(map[string]*document.Node)
indexDocument(resolved.final, Path{}, finalPaths)
snapshotCoverage, snapshotPointers := newResolutionCoverageIndexFromSnapshots(resolved.snapshots)
var deltaCoverage pathPrefixIndex
snapshotIDs := make([]int, len(resolved.deltas))
for id, delta := range resolved.deltas {
if delta.path.valid() {
deltaCoverage.insert(delta.path.String(), id)
}
snapshotIDs[id] = -1
if snapshotID, ok := snapshotCoverage.deepestCoveringID(delta.path); ok {
snapshotIDs[id] = snapshotID
}
}
for snapshotID, snapshot := range resolved.snapshots {
pointer := snapshotPointers[snapshotID]
if info, ok := metadata.fields[pointer]; ok {
info.Origin = cloneOrigin(snapshot.change.Origin)
metadata.fields[pointer] = info
}
}
applyResolutionFieldOrigins(metadata.fields, resolved.snapshots, &deltaCoverage, snapshotIDs)
refreshResolutionFields(metadata.fields, finalPaths, resolved.final, entry, resolved.snapshots, snapshotCoverage)
}
func applyResolutionFieldOrigins(fields map[string]FieldInfo, snapshots []resolutionSnapshot, deltaIndex *pathPrefixIndex, snapshotIDs []int) {
buckets := make([][]string, len(snapshotIDs))
for pointer := range fields {
if _, err := ParsePath(pointer); err != nil {
continue
}
deltaIndex.visitAncestors(pointer, func(deltaID int) {
if snapshotIDs[deltaID] >= 0 {
buckets[deltaID] = append(buckets[deltaID], pointer)
}
})
}
for deltaID, pointers := range buckets {
snapshotID := snapshotIDs[deltaID]
if snapshotID < 0 {
continue
}
origin := snapshots[snapshotID].change.Origin
for _, pointer := range pointers {
info := fields[pointer]
info.Origin = cloneOrigin(origin)
fields[pointer] = info
}
}
}
func refreshResolutionFields(fields map[string]FieldInfo, finalPaths map[string]*document.Node, final *document.Node, entry resolutionInput, snapshots []resolutionSnapshot, snapshotCoverage *resolutionCoverageIndex) {
for pointer := range fields {
if _, ok := finalPaths[pointer]; !ok {
delete(fields, pointer)
}
}
walkDocument(final, Path{}, func(path Path, node *document.Node) {
pointer := path.String()
entryInfo, entryFound := entry.lookup(path)
if info, ok := fields[pointer]; ok {
info.Explicit = entryFound && entryInfo.Explicit
info.Null = node.Kind == document.Null
fields[pointer] = info
return
}
origin := Origin{Layer: LayerResolution}
if snapshotID, ok := snapshotCoverage.deepestCoveringPointerID(pointer); ok {
origin = cloneOrigin(snapshots[snapshotID].change.Origin)
}
fields[pointer] = FieldInfo{
Explicit: entryFound && entryInfo.Explicit,
Null: node.Kind == document.Null,
Origin: origin,
}
})
}