package engine import "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 metadata.inheritance = append([]inheritanceOperation(nil), expansion.operations...) } func historyWithInheritance(path Path, stored []Change, operations []inheritanceOperation) []Change { split := len(stored) for index, change := range stored { if 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 { if operation.control != nil { if !pathsIntersect(operation.control.path, query) { return nil } origin := cloneOrigin(operation.controlOrigin) origin.Operation = ChangeInheritance origin.Inheritance = projectInheritanceSteps(origin) return []Change{{Path: operation.control.path.clone(), Origin: origin, Dependencies: []Path{operation.control.path.clone()}}} } if operation.transfer == nil || !pathsIntersect(operation.transfer.to, query) { return nil } path := query if pathContains(query, operation.transfer.to) { path = operation.transfer.to } var changes []Change parent, parentFound := transferredOperationOrigin(operation, path) if parentFound { parent.Operation = ChangeInheritance parent.Inheritance = projectInheritanceSteps(parent) changes = append(changes, Change{ Path: path.clone(), Origin: parent, Dependencies: []Path{operation.transfer.from.clone(), operation.transfer.reference.clone()}, }) } effective, effectiveFound := operation.effectiveOrigins[path.String()] if effectiveFound { effective = cloneOrigin(effective) effective.Operation = ChangeInheritance } if effectiveFound && (!parentFound || !originContainedBy(effective, parent)) { effective.Inheritance = projectInheritanceSteps(effective) changes = append(changes, Change{ Path: path.clone(), Origin: effective, Dependencies: []Path{operation.transfer.reference.clone()}, }) } else if parentFound && !effectiveFound { removed := cloneOrigin(operation.effectiveOrigins[operation.transfer.to.String()]) removed.Operation = ChangeInheritance removed.Inheritance = projectInheritanceSteps(removed) changes = append(changes, Change{ Path: operation.transfer.to.clone(), Origin: removed, Dependencies: []Path{operation.transfer.reference.clone()}, }) } return changes } func transferredOperationOrigin(operation inheritanceOperation, destination Path) (Origin, bool) { 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 }