Files
configkit/internal/engine/history_select.go

159 lines
4.2 KiB
Go

package engine
import (
"slices"
"sort"
)
type historySelection struct {
path Path
phase historyPhase
payload historyPayloadRef
reportedPath Path
role historyRole
before bool
after bool
attribution historyAttribution
hasAttribution bool
previous []Origin
}
func (model *historyModel) selectHistory(path Path) []historySelection {
if model == nil || !path.valid() || !model.hasObservedShape(path) {
return nil
}
candidateSet := make(map[int]struct{})
for _, observationID := range model.observationsByPointer[path.String()] {
candidateSet[observationID] = struct{}{}
}
model.reportedPathIndex.visitAncestors(path.String(), func(observationID int) {
candidateSet[observationID] = struct{}{}
})
candidates := make([]int, 0, len(candidateSet))
for observationID := range candidateSet {
candidates = append(candidates, observationID)
}
slices.Sort(candidates)
selections := make([]historySelection, 0, len(candidates))
selectionByPayload := make(map[historyPayloadRef]int)
for _, observationID := range candidates {
observation := model.observations[observationID]
if observation.role == historyRoleInventory || !matchesPathShape(path, observation.path) {
continue
}
selectionIndex, found := selectionByPayload[observation.payload]
if !found {
selectionIndex = len(selections)
selectionByPayload[observation.payload] = selectionIndex
selection := historySelection{
path: path.clone(),
phase: observation.phase,
payload: observation.payload,
reportedPath: observation.reportedPath.clone(),
role: observation.role,
}
if attribution, ok := model.resolveAttribution(observation.attributionRef); ok {
selection.attribution = attribution
selection.hasAttribution = true
}
selections = append(selections, selection)
}
selection := &selections[selectionIndex]
selection.before = selection.before || observation.beforePresent
selection.after = selection.after || observation.afterPresent
}
if len(selections) == 0 {
return nil
}
var previous *Origin
for index := range selections {
selection := &selections[index]
if selection.hasAttribution && metadataHistoryRole(selection.role) {
if previous != nil {
selection.previous = []Origin{cloneOrigin(*previous)}
}
origin := cloneOrigin(selection.attribution.origin)
previous = &origin
}
}
return selections
}
func (model *historyModel) hasObservedShape(path Path) bool {
if model == nil || !path.valid() {
return false
}
for _, candidate := range model.shapesByPointer[path.String()] {
if matchesPathShape(path, candidate) {
return true
}
}
return false
}
func (model *historyModel) concreteShapes(pointer string) []Path {
if model == nil {
return nil
}
shapes := model.shapesByPointer[pointer]
cloned := make([]Path, len(shapes))
for index, path := range shapes {
cloned[index] = path.clone()
}
return cloned
}
func (model *historyModel) observedPaths() []Path {
if model == nil {
return nil
}
paths := make([]Path, len(model.shapeOrder))
for index, path := range model.shapeOrder {
paths[index] = path.clone()
}
return paths
}
func (model *historyModel) historicalPaths() []Path {
if model == nil {
return nil
}
pointers := make([]string, 0, len(model.shapesByPointer))
for pointer := range model.shapesByPointer {
pointers = append(pointers, pointer)
}
sort.Strings(pointers)
paths := make([]Path, 0, len(pointers))
for _, pointer := range pointers {
path, err := ParsePath(pointer)
if err == nil {
paths = append(paths, path)
}
}
return paths
}
func (model *historyModel) resolveAttribution(ref historyAttributionRef) (historyAttribution, bool) {
if model == nil || ref < 0 || int(ref) >= len(model.attributions) {
return historyAttribution{}, false
}
attribution := model.attributions[ref]
return historyAttribution{
origin: cloneOrigin(attribution.origin),
reasons: append([]string(nil), attribution.reasons...),
dependencies: clonePaths(attribution.dependencies),
}, true
}
func metadataHistoryRole(role historyRole) bool {
switch role {
case historyRoleDefinition, historyRoleCompositeDefinition, historyRoleOperation, historyRoleControl:
return true
default:
return false
}
}