Files
ConfigKit/internal/engine/inheritance_policy_index.go

124 lines
3.7 KiB
Go

package engine
// inheritancePolicyIndex borrows immutable policy entries. Semantic path
// segments keep mapping keys distinct from sequence indexes. Record-owned
// scopes have their own indexes, just as they have their own policy tables.
type inheritancePolicyIndex struct {
entry *collectionPolicyEntry
children map[pathSegment]*inheritancePolicyIndex
records map[int]*inheritancePolicyIndex
}
func newInheritancePolicyIndex(scope collectionPolicyScope) *inheritancePolicyIndex {
index := &inheritancePolicyIndex{}
for _, entry := range scope.collections {
index.set(entry)
}
return index
}
func (index *inheritancePolicyIndex) set(entry collectionPolicyEntry) {
node := index
for _, segment := range entry.destination.segments {
if node.children == nil {
node.children = make(map[pathSegment]*inheritancePolicyIndex)
}
if node.children[segment] == nil {
node.children[segment] = &inheritancePolicyIndex{}
}
node = node.children[segment]
}
node.entry = &entry
node.records = nil
for position, owner := range entry.policy.recordOrder {
scope := entry.policy.recordPolicies[owner]
if len(scope.collections) == 0 {
continue
}
if node.records == nil {
node.records = make(map[int]*inheritancePolicyIndex)
}
node.records[position] = newInheritancePolicyIndex(scope)
}
}
// localize copies only policies in the requested scope. Earlier record-owned
// policies are considered before direct entries added by destination expansion.
func (index *inheritancePolicyIndex) localize(prefix Path) collectionPolicyScope {
localized := collectionPolicyScope{}
node := index
for offset, segment := range prefix.segments {
if node == nil {
return localized
}
if segment.kind == pathSegmentIndex {
if record := node.records[segment.index]; record != nil {
remainder := Path{segments: prefix.segments[offset+1:]}
for _, entry := range record.localize(remainder).collections {
localized.set(entry.destination, entry.policy)
}
}
}
node = node.children[segment]
}
var collect func(*inheritancePolicyIndex)
collect = func(node *inheritancePolicyIndex) {
if node == nil {
return
}
if node.entry != nil {
relative, _ := relativeHistoryPath(prefix, node.entry.destination)
localized.set(relative, cloneCollectionPolicyForInheritance(node.entry.policy))
}
for _, child := range node.children {
collect(child)
}
}
collect(node)
return localized
}
func (index *inheritancePolicyIndex) absoluteScope(prefix Path) collectionPolicyScope {
scope := collectionPolicyScope{}
for _, entry := range index.localize(prefix).collections {
scope.set(appendDestination(prefix, entry.destination), entry.policy)
}
return scope
}
// replace updates the walker's owned table and its borrowed index together.
// Retirement visits only direct entries below the changed destination.
func (index *inheritancePolicyIndex) replace(scope *collectionPolicyScope, prefix Path, replacement collectionPolicyScope) {
node := index
var parent *inheritancePolicyIndex
var last pathSegment
for _, segment := range prefix.segments {
if node == nil {
break
}
parent, last, node = node, segment, node.children[segment]
}
var remove func(*inheritancePolicyIndex)
remove = func(node *inheritancePolicyIndex) {
if node == nil {
return
}
if node.entry != nil {
delete(scope.collections, node.entry.destination.String())
}
for _, child := range node.children {
remove(child)
}
}
remove(node)
if parent == nil {
*index = inheritancePolicyIndex{}
} else {
delete(parent.children, last)
}
for _, entry := range replacement.collections {
scope.set(entry.destination, entry.policy)
index.set(scope.collections[entry.destination.String()])
}
}