Files
ConfigKit/internal/engine/inheritance.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,
}
}