555 lines
21 KiB
Go
555 lines
21 KiB
Go
package engine
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
|
)
|
|
|
|
type overlayRecord struct {
|
|
change Change
|
|
displaced []displacedDefinition
|
|
}
|
|
|
|
type displacedDefinition struct {
|
|
path Path
|
|
origin Origin
|
|
}
|
|
|
|
func overlayDocuments(base *composedDocument, overlay *document.Node) (*composedDocument, []Diagnostic) {
|
|
if base == nil {
|
|
return nil, invalidOverlayDocument()
|
|
}
|
|
assembly := newAssemblyAccumulator(cloneComposedDocument(base))
|
|
if diagnostics := assembly.applyOverlay(overlay, nil, nil, LayerOverlay); len(diagnostics) != 0 {
|
|
return nil, diagnostics
|
|
}
|
|
return assembly.current, nil
|
|
}
|
|
|
|
func invalidOverlayDocument() []Diagnostic {
|
|
return []Diagnostic{{
|
|
Code: CodeOverlayInvalid, Severity: SeverityError,
|
|
Message: "overlay requires mapping document roots",
|
|
}}
|
|
}
|
|
|
|
func (a *assemblyAccumulator) applyOverlay(overlay *document.Node, incomingOrigins map[string]Origin, applications []historyApplication, layer Layer) []Diagnostic {
|
|
if a == nil || a.current == nil || a.current.root == nil || a.current.root.Kind != document.Mapping || overlay == nil || overlay.Kind != document.Mapping {
|
|
return invalidOverlayDocument()
|
|
}
|
|
mergeOriginContribution(a.current.origins, Path{}, incomingOrigin(incomingOrigins, Path{}, overlay, layer))
|
|
mergeOverlayMappings(a.current, a.current.root, overlay, Path{}, layer, true, incomingOrigins, &a.index)
|
|
if len(applications) == 0 {
|
|
a.current.applications = append(a.current.applications, historyApplication{layer: layer, value: overlay})
|
|
} else {
|
|
for _, application := range applications {
|
|
application.layer = layer
|
|
a.current.applications = append(a.current.applications, application)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// applyOverlayWithCollectionPolicy is the shared recursive-precedence entry for
|
|
// overlays. The caller supplies only the policy and declarations owned by this
|
|
// contribution; layered loading can use the same operation when its lifecycle
|
|
// reaches this boundary.
|
|
func (a *assemblyAccumulator) applyOverlayWithCollectionPolicy(
|
|
overlay *document.Node,
|
|
incomingOrigins map[string]Origin,
|
|
incomingPolicies collectionPolicyScope,
|
|
applications []historyApplication,
|
|
layer Layer,
|
|
sequencePolicy sequencePolicy,
|
|
declarations []sequenceDeclarationSpec,
|
|
) []Diagnostic {
|
|
_, diagnostics := a.applyOverlayWithCollectionProjection(overlay, incomingOrigins, incomingPolicies, applications, layer, sequencePolicy, declarations)
|
|
return diagnostics
|
|
}
|
|
|
|
// applyOverlayWithCollectionProjection also returns the incoming tier's item
|
|
// assignments so acquisition evidence can follow the same operation as values.
|
|
func (a *assemblyAccumulator) applyOverlayWithCollectionProjection(
|
|
overlay *document.Node,
|
|
incomingOrigins map[string]Origin,
|
|
incomingPolicies collectionPolicyScope,
|
|
applications []historyApplication,
|
|
layer Layer,
|
|
sequencePolicy sequencePolicy,
|
|
declarations []sequenceDeclarationSpec,
|
|
) ([]sequenceHistoryContribution, []Diagnostic) {
|
|
if a == nil || a.current == nil || a.current.root == nil || a.current.root.Kind != document.Mapping || overlay == nil || overlay.Kind != document.Mapping {
|
|
return nil, invalidOverlayDocument()
|
|
}
|
|
mergeOriginContribution(a.current.origins, Path{}, incomingOrigin(incomingOrigins, Path{}, overlay, layer))
|
|
sequences, diagnostics := mergeOverlayMappingsWithCollectionPolicy(a, overlay, incomingOrigins, incomingPolicies, layer, sequencePolicy, declarations)
|
|
if len(diagnostics) != 0 {
|
|
return nil, diagnostics
|
|
}
|
|
if len(applications) == 0 {
|
|
application := historyApplication{layer: layer, value: overlay, sequences: sequences}
|
|
application.sequencesByPath = indexSequenceHistoryContributions(application.sequences)
|
|
a.current.applications = append(a.current.applications, application)
|
|
} else {
|
|
for _, application := range projectTierHistoryApplications(applications, sequences) {
|
|
application.layer = layer
|
|
a.current.applications = append(a.current.applications, application)
|
|
}
|
|
}
|
|
return sequences, nil
|
|
}
|
|
|
|
func mergeOverlayMappingsWithCollectionPolicy(
|
|
assembly *assemblyAccumulator,
|
|
overlay *document.Node,
|
|
incomingOrigins map[string]Origin,
|
|
incomingPolicies collectionPolicyScope,
|
|
layer Layer,
|
|
sequencePolicy sequencePolicy,
|
|
declarations []sequenceDeclarationSpec,
|
|
) ([]sequenceHistoryContribution, []Diagnostic) {
|
|
result := assembly.current
|
|
claims := compositionClaims{byPath: make(map[string][]compositionClaim)}
|
|
claims.append(overlay, Path{}, 0)
|
|
identityFieldsByPath, resolvedPolicies, diagnostics := resolveStrictCollectionPolicies(
|
|
claims.ordered, sequencePolicy, result.collectionPolicies, declarations,
|
|
)
|
|
if len(diagnostics) != 0 {
|
|
return nil, diagnostics
|
|
}
|
|
declarationsByPath := make(map[string][]sequenceDeclarationSpec, len(declarations))
|
|
registeredPaths := make(map[string]Path)
|
|
for _, declaration := range declarations {
|
|
key := declaration.destination.String()
|
|
declarationsByPath[key] = append(declarationsByPath[key], declaration)
|
|
registeredPaths[key] = declaration.destination.clone()
|
|
}
|
|
registered := make(map[string]bool)
|
|
for _, claim := range claims.ordered {
|
|
if claim.kind == document.Sequence {
|
|
if _, ok := sequencePolicy.identityFieldsAt(claim.path); ok {
|
|
registered[claim.path.String()] = true
|
|
registeredPaths[claim.path.String()] = claim.path.clone()
|
|
}
|
|
}
|
|
}
|
|
for key := range declarationsByPath {
|
|
registered[key] = true
|
|
}
|
|
for key := range result.collectionPolicies.collections {
|
|
if node, found := documentAtPath(overlay, result.collectionPolicies.collections[key].destination); found && node != nil {
|
|
registered[key] = true
|
|
registeredPaths[key] = result.collectionPolicies.collections[key].destination.clone()
|
|
}
|
|
}
|
|
|
|
resolutions := make(map[string]collectionPolicyResolution, len(registered))
|
|
for key := range registered {
|
|
path := registeredPaths[key]
|
|
policy, hasPolicy := resolvedPolicies.at(path)
|
|
if !hasPolicy {
|
|
continue
|
|
}
|
|
operation := sequenceModeReplace
|
|
if policy.explicitModePresent {
|
|
operation = policy.explicitMode
|
|
} else if len(identityFieldsByPath[key]) != 0 {
|
|
operation = sequenceModeUpsert
|
|
}
|
|
resolutions[key] = collectionPolicyResolution{
|
|
policy: policy, operation: operation,
|
|
effectiveIdentityFields: cloneStrings(identityFieldsByPath[key]),
|
|
}
|
|
}
|
|
|
|
sequenceApplications := make(map[string]sequenceHistoryContribution)
|
|
policyValues := make(map[string]bool)
|
|
var combinationDiagnostics []Diagnostic
|
|
mergeOverlayMappingsWithHooks(result.root, overlay, Path{}, &assembly.index,
|
|
recursiveMergeHooks{
|
|
added: func(path Path, incoming *document.Node) {
|
|
collectOverlayOriginsFrom(result.origins, incomingOrigins, incoming, path, layer)
|
|
},
|
|
combined: func(path Path, incoming *document.Node) {
|
|
mergeOriginContribution(result.origins, path, incomingOrigin(incomingOrigins, path, incoming, layer))
|
|
},
|
|
replaced: func(path Path, existing, incoming *document.Node) {
|
|
assembly.current.collectionPolicies.retireCollectionPoliciesAtOrBelow(path)
|
|
recordOverlayReplacement(result, path, existing, incoming, layer, true, incomingOrigins)
|
|
},
|
|
merge: func(path Path, existing, incoming *document.Node) (*document.Node, bool) {
|
|
resolution, active := resolutions[path.String()]
|
|
if !active {
|
|
return nil, false
|
|
}
|
|
combined, issues := combineSequence(existing, incoming, resolution, sequenceCombinationContext{destination: path})
|
|
if len(issues) != 0 {
|
|
combinationDiagnostics = append(combinationDiagnostics, issues...)
|
|
return existing, false
|
|
}
|
|
applyOverlaySequenceEvidence(result, path, existing, incoming, combined, resolution, incomingOrigins, incomingPolicies, layer)
|
|
policyValues[path.String()] = true
|
|
history := sequenceHistoryContribution{path: path.clone(), mode: sequenceHistoryMapped}
|
|
if resolution.operation == sequenceModeReplace {
|
|
history.mode = sequenceHistoryReplace
|
|
if incoming != nil && incoming.Kind == document.Sequence {
|
|
history.length = len(incoming.Sequence)
|
|
}
|
|
} else {
|
|
history.items = append([]sequenceItemProjection(nil), combined.higherItems...)
|
|
history.replacements = append([]sequenceItemReplacement(nil), combined.replacements...)
|
|
}
|
|
sequenceApplications[path.String()] = history
|
|
return combined.value, true
|
|
},
|
|
},
|
|
)
|
|
if len(combinationDiagnostics) != 0 {
|
|
return nil, canonicalDiagnostics(combinationDiagnostics)
|
|
}
|
|
|
|
for key, grouped := range declarationsByPath {
|
|
if policyValues[key] {
|
|
continue
|
|
}
|
|
path := grouped[0].destination
|
|
applicationIdentity, _ := sequencePolicy.identityFieldsAt(path)
|
|
current, diagnostics := resolveCollectionPolicy(path, grouped, applicationIdentity, nil)
|
|
if len(diagnostics) != 0 {
|
|
combinationDiagnostics = append(combinationDiagnostics, diagnostics...)
|
|
continue
|
|
}
|
|
lower, found := assembly.current.collectionPolicies.at(path)
|
|
var lowerPolicy *collectionPolicy
|
|
if found {
|
|
lowerPolicy = &lower
|
|
}
|
|
retained, _ := documentAtPath(assembly.current.root, path)
|
|
if retained != nil && retained.Kind == document.Sequence && len(current.effectiveIdentityFields) != 0 {
|
|
if _, issues := validateSequenceIdentity(retained, path, current.effectiveIdentityFields, sequenceIdentityDiagnosticCodes{invalid: CodeSequenceInvalid, conflict: CodeSequenceConflict}); len(issues) != 0 {
|
|
combinationDiagnostics = append(combinationDiagnostics, issues...)
|
|
continue
|
|
}
|
|
}
|
|
policy, issues := collectionPolicyWithoutValue(lowerPolicy, current.policy, retained, path)
|
|
if len(issues) != 0 {
|
|
combinationDiagnostics = append(combinationDiagnostics, issues...)
|
|
continue
|
|
}
|
|
assembly.current.collectionPolicies.set(path, policy)
|
|
}
|
|
if len(combinationDiagnostics) != 0 {
|
|
return nil, canonicalDiagnostics(combinationDiagnostics)
|
|
}
|
|
keys := make([]string, 0, len(sequenceApplications))
|
|
for key := range sequenceApplications {
|
|
keys = append(keys, key)
|
|
}
|
|
slices.Sort(keys)
|
|
sequences := make([]sequenceHistoryContribution, 0, len(keys))
|
|
for _, key := range keys {
|
|
sequences = append(sequences, sequenceApplications[key])
|
|
}
|
|
return sequences, nil
|
|
}
|
|
|
|
func mergeOverlayMappings(result *composedDocument, target, overlay *document.Node, current Path, layer Layer, recordReplacements bool, incomingOrigins map[string]Origin, index *mappingIndex) {
|
|
mergeOverlayMappingsWithHooks(target, overlay, current, index, recursiveMergeHooks{
|
|
added: func(path Path, incoming *document.Node) {
|
|
collectOverlayOriginsFrom(result.origins, incomingOrigins, incoming, path, layer)
|
|
},
|
|
combined: func(path Path, incoming *document.Node) {
|
|
mergeOriginContribution(result.origins, path, incomingOrigin(incomingOrigins, path, incoming, layer))
|
|
},
|
|
replaced: func(path Path, existing, incoming *document.Node) {
|
|
displaced := displacedOrigins(existing, result.origins, path)
|
|
previous := cloneOrigin(result.origins[path.String()])
|
|
deleteOriginSubtree(result.origins, path, existing)
|
|
collectOverlayOriginsFrom(result.origins, incomingOrigins, incoming, path, layer)
|
|
if recordReplacements {
|
|
result.overlays = append(result.overlays, overlayRecord{
|
|
change: Change{
|
|
Path: path.clone(), Origin: incomingOrigin(incomingOrigins, path, incoming, layer),
|
|
Previous: previousOrigins(previous),
|
|
},
|
|
displaced: displaced,
|
|
})
|
|
}
|
|
},
|
|
})
|
|
}
|
|
|
|
func mergeOverlayMappingsWithHooks(
|
|
target, overlay *document.Node,
|
|
current Path,
|
|
index *mappingIndex,
|
|
hooks recursiveMergeHooks,
|
|
) {
|
|
mergeRecursiveMappings(target, overlay, current, index, hooks)
|
|
}
|
|
|
|
func recordOverlayReplacement(
|
|
result *composedDocument,
|
|
path Path,
|
|
existing, incoming *document.Node,
|
|
layer Layer,
|
|
recordReplacement bool,
|
|
incomingOrigins map[string]Origin,
|
|
) {
|
|
var displaced []displacedDefinition
|
|
var previous Origin
|
|
if existing != nil {
|
|
displaced = displacedOrigins(existing, result.origins, path)
|
|
previous = cloneOrigin(result.origins[path.String()])
|
|
deleteOriginSubtree(result.origins, path, existing)
|
|
}
|
|
collectOverlayOriginsFrom(result.origins, incomingOrigins, incoming, path, layer)
|
|
if recordReplacement && existing != nil {
|
|
result.overlays = append(result.overlays, overlayRecord{
|
|
change: Change{
|
|
Path: path.clone(), Origin: incomingOrigin(incomingOrigins, path, incoming, layer),
|
|
Previous: previousOrigins(previous),
|
|
},
|
|
displaced: displaced,
|
|
})
|
|
}
|
|
}
|
|
|
|
func applyOverlaySequenceEvidence(
|
|
result *composedDocument,
|
|
path Path,
|
|
lower, higher *document.Node,
|
|
combined sequenceCombinationResult,
|
|
resolution collectionPolicyResolution,
|
|
incomingOrigins map[string]Origin,
|
|
incomingPolicies collectionPolicyScope,
|
|
layer Layer,
|
|
) {
|
|
lowerPolicy, hadLowerPolicy := result.collectionPolicies.at(path)
|
|
if resolution.operation == sequenceModeReplace {
|
|
var lowerValue *collectionPolicy
|
|
if hadLowerPolicy {
|
|
lowerValue = &lowerPolicy
|
|
}
|
|
result.collectionPolicies.retireCollectionPoliciesAtOrBelow(path)
|
|
resolution.policy = collectionPolicyForReplacement(lowerValue, resolution.policy)
|
|
}
|
|
|
|
if combined.value == nil || combined.value.Kind != document.Sequence {
|
|
recordOverlayReplacement(result, path, lower, higher, layer, true, incomingOrigins)
|
|
result.collectionPolicies.set(path, resolution.policy)
|
|
return
|
|
}
|
|
|
|
if resolution.operation == sequenceModeReplace {
|
|
var displaced []displacedDefinition
|
|
var previous Origin
|
|
if lower != nil {
|
|
displaced = displacedOrigins(lower, result.origins, path)
|
|
previous = cloneOrigin(result.origins[path.String()])
|
|
deleteOriginSubtree(result.origins, path, lower)
|
|
}
|
|
mergeOriginContribution(result.origins, path, incomingOrigin(incomingOrigins, path, higher, layer))
|
|
for _, projection := range combined.higherItems {
|
|
if higher != nil && higher.Kind == document.Sequence && projection.inputIndex >= 0 && projection.inputIndex < len(higher.Sequence) {
|
|
source := higher.Sequence[projection.inputIndex]
|
|
collectOverlayOriginsAt(result.origins, incomingOrigins, source, path.Index(projection.inputIndex), path.Index(projection.outputIndex), layer)
|
|
}
|
|
}
|
|
if lower != nil {
|
|
result.overlays = append(result.overlays, overlayRecord{
|
|
change: Change{
|
|
Path: path.clone(), Origin: incomingOrigin(incomingOrigins, path, higher, layer),
|
|
Previous: previousOrigins(previous),
|
|
}, displaced: displaced,
|
|
})
|
|
}
|
|
} else {
|
|
mergeOriginContribution(result.origins, path, incomingOrigin(incomingOrigins, path, higher, layer))
|
|
for _, replacement := range combined.replacements {
|
|
lowerPath := path.Index(replacement.lowerIndex)
|
|
higherPath := path.Index(replacement.higherIndex)
|
|
outputPath := path.Index(replacement.outputIndex)
|
|
if lower == nil || lower.Kind != document.Sequence || replacement.lowerIndex < 0 || replacement.lowerIndex >= len(lower.Sequence) ||
|
|
higher == nil || higher.Kind != document.Sequence || replacement.higherIndex < 0 || replacement.higherIndex >= len(higher.Sequence) {
|
|
continue
|
|
}
|
|
lowerItem := lower.Sequence[replacement.lowerIndex]
|
|
higherItem := higher.Sequence[replacement.higherIndex]
|
|
displaced := displacedOrigins(lowerItem, result.origins, lowerPath)
|
|
previous := cloneOrigin(result.origins[lowerPath.String()])
|
|
deleteOriginSubtree(result.origins, lowerPath, lowerItem)
|
|
collectOverlayOriginsAt(result.origins, incomingOrigins, higherItem, higherPath, outputPath, layer)
|
|
result.overlays = append(result.overlays, overlayRecord{
|
|
change: Change{
|
|
Path: outputPath.clone(), Origin: incomingOrigin(incomingOrigins, higherPath, higherItem, layer),
|
|
Previous: previousOrigins(previous),
|
|
},
|
|
displaced: displaced,
|
|
})
|
|
}
|
|
for _, projection := range combined.higherItems {
|
|
if higher != nil && higher.Kind == document.Sequence && projection.inputIndex >= 0 && projection.inputIndex < len(higher.Sequence) {
|
|
higherItem := higher.Sequence[projection.inputIndex]
|
|
collectOverlayOriginsAt(result.origins, incomingOrigins, higherItem, path.Index(projection.inputIndex), path.Index(projection.outputIndex), layer)
|
|
}
|
|
}
|
|
}
|
|
|
|
policy := cloneCollectionPolicy(resolution.policy)
|
|
policy.recordOrder = nil
|
|
policy.recordPolicies = nil
|
|
lowerProjection := newSequenceItemProjectionIndex(combined.lowerItems)
|
|
higherProjection := newSequenceItemProjectionIndex(combined.higherItems)
|
|
incomingCollection, hasIncomingCollection := incomingPolicies.at(path)
|
|
for outputIndex := range combined.value.Sequence {
|
|
if higherIndex, replaced := higherProjection.inputForOutput(outputIndex); replaced {
|
|
if record, found := incomingCollection.recordPolicyAt(higherIndex); hasIncomingCollection && found {
|
|
policy.setRecordPolicyAt(outputIndex, retainedCollectionRecordPolicy(record))
|
|
} else {
|
|
policy.setRecordPolicyAt(outputIndex, replacedCollectionRecordPolicy())
|
|
}
|
|
continue
|
|
}
|
|
if hadLowerPolicy && resolution.operation != sequenceModeReplace {
|
|
if lowerIndex, retained := lowerProjection.inputForOutput(outputIndex); retained {
|
|
if record, found := lowerPolicy.recordPolicyAt(lowerIndex); found {
|
|
policy.setRecordPolicyAt(outputIndex, retainedCollectionRecordPolicy(record))
|
|
}
|
|
}
|
|
} else {
|
|
policy.setRecordPolicyAt(outputIndex, replacedCollectionRecordPolicy())
|
|
}
|
|
}
|
|
result.collectionPolicies.set(path, policy)
|
|
}
|
|
|
|
func collectOverlayOrigins(origins map[string]Origin, node *document.Node, path Path, layer Layer) {
|
|
collectOverlayOriginsFrom(origins, nil, node, path, layer)
|
|
}
|
|
|
|
func collectOverlayOriginsFrom(origins, incomingOrigins map[string]Origin, node *document.Node, path Path, layer Layer) {
|
|
collectOverlayOriginsAt(origins, incomingOrigins, node, path, path, layer)
|
|
}
|
|
|
|
func collectOverlayOriginsAt(origins, incomingOrigins map[string]Origin, node *document.Node, from, to Path, layer Layer) {
|
|
walkDocument(node, Path{}, func(relative Path, value *document.Node) {
|
|
source := appendDestination(from, relative)
|
|
destination := appendDestination(to, relative)
|
|
origins[destination.String()] = incomingOrigin(incomingOrigins, source, value, layer)
|
|
})
|
|
}
|
|
|
|
func mergeOverlayOrigin(origins map[string]Origin, path Path, location document.Location, layer Layer) {
|
|
mergeOriginContribution(origins, path, overlayOrigin(layer, location))
|
|
}
|
|
|
|
func mergeOriginContribution(origins map[string]Origin, path Path, incoming Origin) {
|
|
pointer := path.String()
|
|
origin := origins[pointer]
|
|
origin.Layer = incoming.Layer
|
|
origin.Locations = append(origin.Locations, incoming.Locations...)
|
|
origins[pointer] = origin
|
|
}
|
|
|
|
func incomingOrigin(origins map[string]Origin, path Path, node *document.Node, layer Layer) Origin {
|
|
if origin, ok := origins[path.String()]; ok {
|
|
origin = cloneOrigin(origin)
|
|
origin.Layer = layer
|
|
return origin
|
|
}
|
|
return overlayOrigin(layer, node.Location)
|
|
}
|
|
|
|
func overlayOrigin(layer Layer, location document.Location) Origin {
|
|
return Origin{Layer: layer, Locations: []Location{publicLocation(location)}}
|
|
}
|
|
|
|
func previousOrigins(origin Origin) []Origin {
|
|
if origin.Layer == "" {
|
|
return nil
|
|
}
|
|
return []Origin{origin}
|
|
}
|
|
|
|
func displacedOrigins(node *document.Node, origins map[string]Origin, path Path) []displacedDefinition {
|
|
var displaced []displacedDefinition
|
|
collectDisplacedOrigins(&displaced, node, origins, path, Origin{})
|
|
return sortDisplacedDefinitions(displaced)
|
|
}
|
|
|
|
func sortDisplacedDefinitions(displaced []displacedDefinition) []displacedDefinition {
|
|
type orderedDefinition struct {
|
|
definition displacedDefinition
|
|
pointer string
|
|
}
|
|
ordered := make([]orderedDefinition, len(displaced))
|
|
for i, definition := range displaced {
|
|
ordered[i] = orderedDefinition{definition: definition, pointer: definition.path.String()}
|
|
}
|
|
slices.SortFunc(ordered, func(left, right orderedDefinition) int {
|
|
return strings.Compare(left.pointer, right.pointer)
|
|
})
|
|
for i, item := range ordered {
|
|
displaced[i] = item.definition
|
|
}
|
|
return displaced
|
|
}
|
|
|
|
func collectDisplacedOrigins(displaced *[]displacedDefinition, node *document.Node, origins map[string]Origin, path Path, inherited Origin) {
|
|
origin, ok := origins[path.String()]
|
|
if !ok {
|
|
origin = cloneOrigin(inherited)
|
|
origin.Locations = []Location{publicLocation(node.Location)}
|
|
}
|
|
*displaced = append(*displaced, displacedDefinition{path: path.clone(), origin: cloneOrigin(origin)})
|
|
switch node.Kind {
|
|
case document.Mapping:
|
|
for _, entry := range node.Mapping {
|
|
collectDisplacedOrigins(displaced, entry.Value, origins, path.Key(entry.Key), origin)
|
|
}
|
|
case document.Sequence:
|
|
for index, child := range node.Sequence {
|
|
collectDisplacedOrigins(displaced, child, origins, path.Index(index), origin)
|
|
}
|
|
}
|
|
}
|
|
|
|
func deleteOriginSubtree(origins map[string]Origin, path Path, node *document.Node) {
|
|
delete(origins, path.String())
|
|
switch node.Kind {
|
|
case document.Mapping:
|
|
for _, entry := range node.Mapping {
|
|
deleteOriginSubtree(origins, path.Key(entry.Key), entry.Value)
|
|
}
|
|
case document.Sequence:
|
|
for index, child := range node.Sequence {
|
|
deleteOriginSubtree(origins, path.Index(index), child)
|
|
}
|
|
}
|
|
}
|
|
|
|
func cloneOriginIndex(origins map[string]Origin) map[string]Origin {
|
|
cloned := make(map[string]Origin, len(origins))
|
|
for pointer, origin := range origins {
|
|
cloned[pointer] = cloneOrigin(origin)
|
|
}
|
|
return cloned
|
|
}
|
|
|
|
func cloneOverlayRecords(records []overlayRecord) []overlayRecord {
|
|
cloned := make([]overlayRecord, len(records))
|
|
for index, record := range records {
|
|
cloned[index].change = cloneChange(record.change)
|
|
cloned[index].displaced = make([]displacedDefinition, len(record.displaced))
|
|
for displacedIndex, displaced := range record.displaced {
|
|
cloned[index].displaced[displacedIndex] = displacedDefinition{
|
|
path: displaced.path.clone(), origin: cloneOrigin(displaced.origin),
|
|
}
|
|
}
|
|
}
|
|
return cloned
|
|
}
|