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, } }) }