Files
ConfigKit/internal/engine/inheritance_origin.go

128 lines
4.2 KiB
Go

package engine
import (
"slices"
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
)
func directInheritanceOrigins(node *document.Node, root Path, input map[string]Origin) map[string]Origin {
result := make(map[string]Origin)
walkDocument(node, root, func(path Path, value *document.Node) {
origin, found := input[path.String()]
if !found {
origin = Origin{Layer: LayerBase, Locations: []Location{publicLocation(value.Location)}}
}
origin = cloneOrigin(origin)
if len(origin.contributors) == 0 {
origin.contributors = make([]inheritanceContributor, len(origin.Locations))
for index, location := range origin.Locations {
origin.contributors[index] = inheritanceContributor{
source: path.clone(), location: location,
}
}
}
result[path.String()] = origin
})
return result
}
func transferredInheritanceOrigins(parent map[string]Origin, from, to Path, control inheritanceTransfer) map[string]Origin {
result := make(map[string]Origin, len(parent))
transfer := control // one immutable edge shared by every matching contributor
links := make(map[*inheritanceTrace]*inheritanceTrace)
for pointer, origin := range parent {
path, err := ParsePath(pointer)
if err != nil {
continue
}
relative, ok := relativeHistoryPath(from, path)
if !ok {
continue
}
destination := appendInheritancePath(to, relative)
origin = cloneOrigin(origin)
for index, contributor := range origin.contributors {
link, found := links[contributor.trace]
if !found {
link = &inheritanceTrace{previous: contributor.trace, transfer: &transfer}
links[contributor.trace] = link
}
origin.contributors[index].trace = link
}
result[destination.String()] = origin
}
return result
}
func appendInheritancePath(root, relative Path) Path {
return Path{segments: append(clonePathSegments(root.segments, len(relative.segments)), relative.segments...)}
}
func mergeInheritanceOrigin(origins map[string]Origin, path Path, incoming Origin) {
pointer := path.String()
current := origins[pointer]
current.Layer = incoming.Layer
current.Locations = append(current.Locations, incoming.Locations...)
current.contributors = append(current.contributors, incoming.contributors...)
origins[pointer] = current
}
func copyInheritanceSubtree(target, incoming map[string]Origin, node *document.Node, root Path) {
walkDocument(node, root, func(path Path, _ *document.Node) {
target[path.String()] = cloneOrigin(incoming[path.String()])
})
}
// Materialize concrete steps only for an inspection record. The linked private
// traces remain compact when one parent supplies many descendant values.
func projectInheritanceSteps(origin Origin) []InheritanceStep {
steps := cloneInheritanceSteps(origin.Inheritance)
for _, contributor := range origin.contributors {
var reverse []*inheritanceTrace
for link := contributor.trace; link != nil; link = link.previous {
reverse = append(reverse, link)
}
current := contributor.source
for index := len(reverse) - 1; index >= 0; index-- {
transfer := reverse[index].transfer
relative, ok := relativeHistoryPath(transfer.from, current)
if !ok {
continue
}
next := appendInheritancePath(transfer.to, relative)
step := InheritanceStep{
From: current.clone(), To: next.clone(), Reference: transfer.reference.clone(),
Locations: cloneLocations(transfer.locations),
}
if !slices.ContainsFunc(steps, func(existing InheritanceStep) bool {
return existing.From.String() == step.From.String() &&
existing.To.String() == step.To.String() &&
existing.Reference.String() == step.Reference.String() &&
slices.Equal(existing.Locations, step.Locations)
}) {
steps = append(steps, step)
}
current = next
}
}
return steps
}
func sameInheritanceTrace(left, right *inheritanceTrace) bool {
for left != nil && right != nil {
if left == right {
return true
}
l, r := left.transfer, right.transfer
if !slices.Equal(l.from.segments, r.from.segments) ||
!slices.Equal(l.to.segments, r.to.segments) ||
!slices.Equal(l.reference.segments, r.reference.segments) ||
!slices.Equal(l.locations, r.locations) {
return false
}
left, right = left.previous, right.previous
}
return left == nil && right == nil
}