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 { localized := collectionPolicyScope{} for _, entry := range scope.collections { if relative, inside := relativeHistoryPath(prefix, entry.destination); inside { localized.set(relative, cloneCollectionPolicyForInheritance(entry.policy)) continue } relative, inside := relativeHistoryPath(entry.destination, prefix) if !inside || len(relative.segments) == 0 || relative.segments[0].kind != pathSegmentIndex { continue } record, found := entry.policy.recordPolicyAt(relative.segments[0].index) if !found { continue } remainder := Path{segments: append([]pathSegment(nil), relative.segments[1:]...)} for _, nested := range inheritancePolicyScopeAtPath(record.scope, remainder).collections { localized.set(nested.destination, cloneCollectionPolicyForInheritance(nested.policy)) } } return localized } 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, } }