165 lines
5.8 KiB
Go
165 lines
5.8 KiB
Go
package engine
|
|
|
|
import "gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
|
|
|
type historyCheckpointPayload struct {
|
|
root *document.Node
|
|
origins map[string]Origin
|
|
}
|
|
|
|
func (builder *historyBuilder) observePreCLICheckpoint(root *document.Node, origins map[string]Origin) {
|
|
builder.observeTypedCheckpoint(historyPhasePreCLI, root, origins, "applied retained defaults and canonical typed decoding")
|
|
}
|
|
|
|
func (builder *historyBuilder) observePostCLICheckpoint(root *document.Node, origins map[string]Origin) {
|
|
builder.observeTypedCheckpoint(historyPhasePostCLI, root, origins, "applied the typed CLI assignment")
|
|
}
|
|
|
|
func (builder *historyBuilder) observeTypedCheckpoint(phase historyPhase, root *document.Node, origins map[string]Origin, reason string) {
|
|
builder.requireOpen()
|
|
if phase != historyPhasePreCLI && phase != historyPhasePostCLI {
|
|
panic("configkit: typed checkpoint has an invalid history phase")
|
|
}
|
|
payload := builder.nextPayload(phase)
|
|
checkpoint := historyCheckpointPayload{root: root, origins: cloneOriginIndex(origins)}
|
|
switch phase {
|
|
case historyPhasePreCLI:
|
|
if builder.model.preCLICheckpoint != nil || payload.id != 0 {
|
|
panic("configkit: pre-CLI checkpoint was already registered")
|
|
}
|
|
builder.model.preCLICheckpoint = &checkpoint
|
|
case historyPhasePostCLI:
|
|
if builder.model.postCLICheckpoint != nil || payload.id != 0 {
|
|
panic("configkit: post-CLI checkpoint was already registered")
|
|
}
|
|
builder.model.postCLICheckpoint = &checkpoint
|
|
}
|
|
|
|
beforeRoot := builder.scratch
|
|
paths := unionCLIHistoryPaths(beforeRoot, root)
|
|
for _, path := range paths {
|
|
beforeNode, before := documentAtPath(beforeRoot, path)
|
|
afterNode, after := documentAtPath(root, path)
|
|
sensitive := beforeNode != nil && beforeNode.Sensitive || afterNode != nil && afterNode.Sensitive
|
|
builder.addObservation(historyObservation{
|
|
path: path, phase: phase, payload: payload,
|
|
reportedPath: path, role: historyRoleInventory,
|
|
sensitive: sensitive, attributionRef: noHistoryAttribution,
|
|
})
|
|
if configurationValuesEqual(beforeNode, afterNode) {
|
|
continue
|
|
}
|
|
attributionRef := noHistoryAttribution
|
|
if after {
|
|
origin, found := origins[path.String()]
|
|
if !found {
|
|
origin = Origin{Layer: LayerDefault}
|
|
}
|
|
origin.Operation = ChangeDecoding
|
|
attributionRef = builder.addAttribution(origin, []string{reason}, nil)
|
|
}
|
|
builder.addObservation(historyObservation{
|
|
path: path, phase: phase, payload: payload,
|
|
reportedPath: path, role: historyRoleCheckpoint,
|
|
beforePresent: before, afterPresent: after, sensitive: sensitive,
|
|
attributionRef: attributionRef,
|
|
})
|
|
}
|
|
builder.advanceScratch(document.Clone(root))
|
|
}
|
|
|
|
func (builder *historyBuilder) observeCLIEvidence(applications []historyApplication, records []overlayRecord) {
|
|
builder.requireOpen()
|
|
count := max(len(applications), len(records))
|
|
if count == 0 {
|
|
return
|
|
}
|
|
index := new(mappingIndex)
|
|
for item := 0; item < count; item++ {
|
|
var applicationRef historyPayloadRef
|
|
hasApplication := false
|
|
if item < len(applications) {
|
|
application := applications[item]
|
|
applicationRef = builder.addCLIPayload(&application, nil)
|
|
hasApplication = true
|
|
walkHistoryApplication(application, func(_, effective Path, node *document.Node) {
|
|
origin := sourceApplicationOrigin(application, node)
|
|
attribution := builder.addAttribution(origin, nil, nil)
|
|
builder.addObservation(historyObservation{
|
|
path: effective, phase: historyPhaseCLI, payload: applicationRef,
|
|
reportedPath: effective, role: historyRoleContribution,
|
|
afterPresent: true, sensitive: node.Sensitive,
|
|
attributionRef: attribution,
|
|
})
|
|
})
|
|
applyHistoryApplication(&builder.scratch, application, index)
|
|
}
|
|
if item < len(records) {
|
|
record := records[item]
|
|
payload := builder.addCLIPayload(nil, &record)
|
|
if hasApplication {
|
|
owned := &builder.model.cliPayloads[payload.id]
|
|
owned.applicationRef = applicationRef
|
|
owned.hasApplicationRef = true
|
|
}
|
|
attribution := builder.addAttribution(record.change.Origin, nil, nil)
|
|
for _, displaced := range record.displaced {
|
|
builder.addObservation(historyObservation{
|
|
path: displaced.path, phase: historyPhaseCLI, payload: payload,
|
|
reportedPath: record.change.Path, role: historyRoleOperation,
|
|
beforePresent: true, attributionRef: attribution,
|
|
})
|
|
}
|
|
for _, introduced := range record.introduced {
|
|
builder.addObservation(historyObservation{
|
|
path: introduced.path, phase: historyPhaseCLI, payload: payload,
|
|
reportedPath: record.change.Path, role: historyRoleOperation,
|
|
afterPresent: true, sensitive: introduced.sensitive,
|
|
attributionRef: attribution,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (builder *historyBuilder) addCLIPayload(application *historyApplication, record *overlayRecord) historyPayloadRef {
|
|
payload := builder.nextPayload(historyPhaseCLI)
|
|
if payload.id != len(builder.model.cliPayloads) {
|
|
panic("configkit: CLI payload identifiers are out of order")
|
|
}
|
|
owned := historySourcePayload{}
|
|
switch {
|
|
case application != nil:
|
|
owned.kind = historySourceApplicationPayload
|
|
owned.application = cloneHistoryApplications([]historyApplication{*application})[0]
|
|
case record != nil:
|
|
owned.kind = historySourceOverlayPayload
|
|
owned.overlay = cloneOverlayRecords([]overlayRecord{*record})[0]
|
|
default:
|
|
panic("configkit: CLI payload requires an application or operation")
|
|
}
|
|
builder.model.cliPayloads = append(builder.model.cliPayloads, owned)
|
|
return payload
|
|
}
|
|
|
|
func unionCLIHistoryPaths(roots ...*document.Node) []Path {
|
|
var paths []Path
|
|
seen := make(map[string][]Path)
|
|
for _, root := range roots {
|
|
if root == nil {
|
|
continue
|
|
}
|
|
walkDocument(root, Path{}, func(path Path, _ *document.Node) {
|
|
for _, existing := range seen[path.String()] {
|
|
if sameConcreteHistoryPath(existing, path) {
|
|
return
|
|
}
|
|
}
|
|
owned := path.clone()
|
|
paths = append(paths, owned)
|
|
seen[path.String()] = append(seen[path.String()], owned)
|
|
})
|
|
}
|
|
return paths
|
|
}
|