Track the main version through sequence movement
This commit is contained in:
107
internal/engine/version_projection.go
Normal file
107
internal/engine/version_projection.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package engine
|
||||
|
||||
import "gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
||||
|
||||
func sequenceVersionPath(rule *VersionRule) bool {
|
||||
if rule == nil {
|
||||
return false
|
||||
}
|
||||
for _, segment := range rule.Path.segments {
|
||||
if segment.kind == pathSegmentIndex {
|
||||
return true
|
||||
}
|
||||
if segment.kind == pathSegmentParsed {
|
||||
if _, index := schemaIndex(segment); index {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Source-local checking and evidence keep their original coordinates. The
|
||||
// effective rule follows the main declaration through structural placement.
|
||||
func (state *sourceVersionState) locateMainVersion(root *document.Node) {
|
||||
if state == nil || state.effectiveRule == nil {
|
||||
return
|
||||
}
|
||||
path := state.effectiveRule.Path
|
||||
if state.mainProcessing != nil && state.mainProcessing.evidence != nil {
|
||||
path = state.mainProcessing.evidence.sensitivityPaths()[0]
|
||||
} else if root != nil {
|
||||
path = resolveSourceVersionPath(root, path).resolvedPath
|
||||
}
|
||||
rule := *state.effectiveRule
|
||||
rule.Path = path.clone()
|
||||
state.effectiveRule = &rule
|
||||
}
|
||||
|
||||
func (state *sourceVersionState) projectMainVersion(operations []sequenceHistoryContribution) {
|
||||
if state == nil {
|
||||
return
|
||||
}
|
||||
state.effectiveRule = projectVersionRule(state.effectiveRule, operations)
|
||||
}
|
||||
|
||||
func projectVersionRule(rule *VersionRule, operations []sequenceHistoryContribution) *VersionRule {
|
||||
if rule == nil || len(operations) == 0 {
|
||||
return rule
|
||||
}
|
||||
path, found := projectSequenceOperationPath(rule.Path, indexSequenceHistoryContributions(operations))
|
||||
if !found {
|
||||
return rule
|
||||
}
|
||||
projected := *rule
|
||||
projected.Path = path
|
||||
return &projected
|
||||
}
|
||||
|
||||
func versionClaimFromApplication(application historyApplication, rule *VersionRule) *document.Node {
|
||||
if rule == nil {
|
||||
return nil
|
||||
}
|
||||
operation, affected := applicationOperationPath(application, rule.Path, nil)
|
||||
if !affected {
|
||||
return nil
|
||||
}
|
||||
local, found := historyApplicationLocalPath(application, operation)
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
value, _ := documentAtPath(application.value, local)
|
||||
return value
|
||||
}
|
||||
|
||||
// Underlays precede the main tier, so missing fields are not removals of the
|
||||
// later main declaration. Check authored claims in each source's assigned
|
||||
// coordinates, without interpreting a whole-list initialization as a removal.
|
||||
func projectedUnderlayVersionDiagnostic(applications []historyApplication, sources []SourceInfo, rule *VersionRule) *Diagnostic {
|
||||
if rule == nil {
|
||||
return nil
|
||||
}
|
||||
byID := make(map[string]SourceInfo, len(sources))
|
||||
for _, source := range sources {
|
||||
byID[source.ID] = source
|
||||
}
|
||||
for _, application := range applications {
|
||||
if application.layer != LayerUnderlay || application.value == nil {
|
||||
continue
|
||||
}
|
||||
var node *document.Node
|
||||
if pathContains(rule.Path, application.path) {
|
||||
node = application.value
|
||||
} else if local, found := historyApplicationLocalPath(application, rule.Path); found {
|
||||
node = fragmentVersionClaim(application.value, &VersionRule{Path: local})
|
||||
}
|
||||
if node == nil {
|
||||
continue
|
||||
}
|
||||
code, message := CodeUnderlayVersion, "underlay inputs cannot contribute to the schema-version path"
|
||||
if byID[application.value.Location.SourceID].ImportedFrom != nil {
|
||||
code, message = CodeImportVersion, "imported fragments cannot contribute to the schema-version path"
|
||||
}
|
||||
location := publicLocation(node.Location)
|
||||
return &Diagnostic{Code: code, Severity: SeverityError, Path: rule.Path.clone(), Message: message, Primary: &location}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user