198 lines
6.9 KiB
Go
198 lines
6.9 KiB
Go
package engine
|
|
|
|
import "gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
|
|
|
const inheritanceKey = "extends"
|
|
|
|
// These private records keep structural evidence separate from acquired source
|
|
// records. Later inspection can project a transfer onto individual descendants.
|
|
type inheritanceTransfer struct {
|
|
from Path
|
|
to Path
|
|
reference Path
|
|
locations []Location
|
|
policyTemplates []collectionPolicyScope
|
|
templatePolicies collectionPolicyScope
|
|
projectedPolicies []projectedCollectionPolicy
|
|
}
|
|
|
|
type inheritanceControl struct {
|
|
path Path
|
|
locations []Location
|
|
cancelled bool
|
|
}
|
|
|
|
type inheritanceScope struct {
|
|
root *document.Node
|
|
origins map[string]Origin
|
|
collectionPolicies collectionPolicyScope
|
|
transfers []inheritanceTransfer
|
|
controls []inheritanceControl
|
|
replacements []inheritanceReplacement
|
|
operations []inheritanceOperation
|
|
}
|
|
|
|
type inheritanceOperation struct {
|
|
control *inheritanceControl
|
|
transfer *inheritanceTransfer
|
|
controlOrigin Origin
|
|
parent *document.Node
|
|
effective *document.Node
|
|
parentOrigins map[string]Origin
|
|
effectiveOrigins map[string]Origin
|
|
parentPolicies []collectionPolicyScope
|
|
templatePolicies collectionPolicyScope
|
|
effectivePolicies []collectionPolicyScope
|
|
sequences []sequenceHistoryContribution
|
|
identityFields []string
|
|
}
|
|
|
|
func cloneInheritancePolicyTemplates(templates []collectionPolicyScope) []collectionPolicyScope {
|
|
if templates == nil {
|
|
return nil
|
|
}
|
|
cloned := make([]collectionPolicyScope, len(templates))
|
|
for index, template := range templates {
|
|
cloned[index] = cloneCollectionPolicyScopeForInheritance(template)
|
|
}
|
|
return cloned
|
|
}
|
|
|
|
func cloneProjectedInheritancePolicies(policies []projectedCollectionPolicy) []projectedCollectionPolicy {
|
|
if policies == nil {
|
|
return nil
|
|
}
|
|
cloned := make([]projectedCollectionPolicy, len(policies))
|
|
for index, policy := range policies {
|
|
cloned[index] = projectedCollectionPolicy{
|
|
path: policy.path.clone(), policy: cloneCollectionPolicyForInheritance(policy.policy),
|
|
}
|
|
}
|
|
return cloned
|
|
}
|
|
|
|
func cloneInheritanceTransfer(transfer inheritanceTransfer) inheritanceTransfer {
|
|
cloned := transfer
|
|
cloned.from = transfer.from.clone()
|
|
cloned.to = transfer.to.clone()
|
|
cloned.reference = transfer.reference.clone()
|
|
cloned.locations = append([]Location(nil), transfer.locations...)
|
|
cloned.policyTemplates = cloneInheritancePolicyTemplates(transfer.policyTemplates)
|
|
cloned.templatePolicies = cloneCollectionPolicyScopeForInheritance(transfer.templatePolicies)
|
|
cloned.projectedPolicies = cloneProjectedInheritancePolicies(transfer.projectedPolicies)
|
|
return cloned
|
|
}
|
|
|
|
func cloneInheritanceOperations(operations []inheritanceOperation) []inheritanceOperation {
|
|
if operations == nil {
|
|
return nil
|
|
}
|
|
cloned := make([]inheritanceOperation, len(operations))
|
|
for index, operation := range operations {
|
|
cloned[index] = operation
|
|
if operation.control != nil {
|
|
control := *operation.control
|
|
control.path = operation.control.path.clone()
|
|
control.locations = append([]Location(nil), operation.control.locations...)
|
|
cloned[index].control = &control
|
|
}
|
|
if operation.transfer != nil {
|
|
transfer := cloneInheritanceTransfer(*operation.transfer)
|
|
cloned[index].transfer = &transfer
|
|
}
|
|
cloned[index].parent = document.Clone(operation.parent)
|
|
cloned[index].effective = document.Clone(operation.effective)
|
|
cloned[index].parentOrigins = cloneOriginIndex(operation.parentOrigins)
|
|
cloned[index].effectiveOrigins = cloneOriginIndex(operation.effectiveOrigins)
|
|
cloned[index].parentPolicies = cloneInheritancePolicyTemplates(operation.parentPolicies)
|
|
cloned[index].templatePolicies = cloneCollectionPolicyScopeForInheritance(operation.templatePolicies)
|
|
cloned[index].effectivePolicies = cloneInheritancePolicyTemplates(operation.effectivePolicies)
|
|
cloned[index].sequences = cloneSequenceHistoryContributions(operation.sequences)
|
|
cloned[index].identityFields = cloneStrings(operation.identityFields)
|
|
}
|
|
return cloned
|
|
}
|
|
|
|
func inheritancePolicyScopeAtPath(scope collectionPolicyScope, prefix Path) collectionPolicyScope {
|
|
return newInheritancePolicyIndex(scope).localize(prefix)
|
|
}
|
|
|
|
func projectInheritancePolicyTemplates(templates []collectionPolicyScope, prefix Path) []projectedCollectionPolicy {
|
|
var projected []projectedCollectionPolicy
|
|
for _, template := range templates {
|
|
for _, policy := range projectCollectionPolicyScope(template, prefix) {
|
|
projected = append(projected, projectedCollectionPolicy{
|
|
path: policy.path.clone(), policy: cloneCollectionPolicyForInheritance(policy.policy),
|
|
})
|
|
}
|
|
}
|
|
return projected
|
|
}
|
|
|
|
// A replacement retains the displaced source/derivation record while the
|
|
// effective origin belongs to the child. Value trees are borrowed immutable
|
|
// inputs or detached templates and are budgeted with retained history later.
|
|
type inheritanceReplacement struct {
|
|
path Path
|
|
before *document.Node
|
|
after *document.Node
|
|
previous []displacedDefinition
|
|
effective Origin
|
|
}
|
|
|
|
// A link represents one shared relationship edge. Each contributor retains
|
|
// its original semantic path independently of source line/column coordinates.
|
|
type inheritanceTrace struct {
|
|
previous *inheritanceTrace
|
|
transfer *inheritanceTransfer
|
|
}
|
|
|
|
type inheritanceContributor struct {
|
|
source Path
|
|
location Location
|
|
trace *inheritanceTrace
|
|
}
|
|
|
|
// expandSiblingDefinitions resolves only the mappings directly in scope.
|
|
// Nested instructions remain in the returned templates for destination-local
|
|
// resolution. This private entry point does not participate in public loading.
|
|
func expandSiblingDefinitions(scope *document.Node, scopePath Path) (*inheritanceScope, []Diagnostic) {
|
|
return expandSiblingDefinitionsWithLimits(scope, scopePath, defaultProjectionLimits())
|
|
}
|
|
|
|
func expandSiblingDefinitionsWithLimits(scope *document.Node, scopePath Path, limits projectionLimits) (*inheritanceScope, []Diagnostic) {
|
|
return expandSiblingDefinitionsWithOrigins(scope, scopePath, limits, nil)
|
|
}
|
|
|
|
func inheritanceDirective(node *document.Node) (document.Entry, bool) {
|
|
for _, entry := range node.Mapping {
|
|
if entry.Key == inheritanceKey {
|
|
return entry, true
|
|
}
|
|
}
|
|
return document.Entry{}, false
|
|
}
|
|
|
|
func inheritanceDirectiveLocation(node *document.Node) Location {
|
|
entry, _ := inheritanceDirective(node)
|
|
return publicLocation(entry.Value.Location)
|
|
}
|
|
|
|
func withoutInheritanceDirective(node *document.Node) *document.Node {
|
|
payload := *node
|
|
payload.Mapping = make([]document.Entry, 0, len(node.Mapping))
|
|
for _, entry := range node.Mapping {
|
|
if entry.Key != inheritanceKey {
|
|
payload.Mapping = append(payload.Mapping, entry)
|
|
}
|
|
}
|
|
return &payload
|
|
}
|
|
|
|
func inheritanceDiagnostic(code Code, path Path, primary Location, related []Location, message string) Diagnostic {
|
|
return Diagnostic{
|
|
Code: code, Severity: SeverityError, Path: path, Message: message,
|
|
Primary: &primary, Related: related,
|
|
}
|
|
}
|