Files
configkit/internal/engine/inheritance_history.go

88 lines
3.0 KiB
Go

package engine
import (
"slices"
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
)
// Retain source history at original paths. Structural operations are held once
// per relationship and projected onto a requested destination path only when
// History or Explain is queried.
func applyInheritanceMetadata(metadata *Metadata, raw *document.Node, rawOrigins map[string]Origin, overlays []overlayRecord, expansion *inheritanceScope) {
if metadata == nil || expansion == nil {
return
}
sourceView := Metadata{
fields: make(map[string]FieldInfo),
history: metadata.history,
}
walkDocument(raw, Path{}, func(path Path, node *document.Node) {
origin, found := rawOrigins[path.String()]
if !found {
origin = Origin{Layer: LayerBase, Locations: []Location{publicLocation(node.Location)}}
}
sourceView.fields[path.String()] = FieldInfo{Explicit: true, Null: node.Kind == document.Null, Origin: cloneOrigin(origin)}
})
applyOverlayMetadata(&sourceView, overlays)
metadata.history = sourceView.history
}
func historyWithInheritance(path Path, stored []Change, operations []inheritanceOperation) []Change {
split := len(stored)
for index, change := range stored {
if change.Origin.Layer == LayerCLI || change.Origin.Layer == LayerResolution {
split = index
break
}
}
history := cloneChanges(stored[:split])
for _, operation := range operations {
for _, change := range projectedInheritanceChanges(path, operation) {
if len(history) != 0 {
change.Previous = []Origin{cloneOrigin(history[len(history)-1].Origin)}
}
history = append(history, change)
}
}
for _, change := range stored[split:] {
cloned := cloneChange(change)
if len(history) != 0 {
cloned.Previous = []Origin{cloneOrigin(history[len(history)-1].Origin)}
}
history = append(history, cloned)
}
return history
}
func projectedInheritanceChanges(query Path, operation inheritanceOperation) []Change {
return inheritanceChangesForQuery(query, operation)
}
func transferredOperationOrigin(operation inheritanceOperation, destination Path) (Origin, bool) {
if inheritanceOperationIdentity(operation, destination) {
return Origin{}, false
}
relative, ok := relativeHistoryPath(operation.transfer.to, destination)
if !ok {
return Origin{}, false
}
source := appendInheritancePath(operation.transfer.from, relative)
origin, found := operation.parentOrigins[source.String()]
if !found {
return Origin{}, false
}
transferred := transferredInheritanceOrigins(map[string]Origin{source.String(): origin},
operation.transfer.from, operation.transfer.to, *operation.transfer)
return transferred[destination.String()], true
}
func inheritanceOperationIdentity(operation inheritanceOperation, path Path) bool {
if operation.transfer == nil || len(operation.identityFields) == 0 {
return false
}
relative, within := relativeHistoryPath(operation.transfer.to, path)
return within && len(relative.segments) != 0 && relative.segments[0].kind != pathSegmentIndex &&
slices.Contains(operation.identityFields, relative.segments[0].key)
}