package engine import "gitea.maximumdirect.net/eric/ConfigKit/internal/document" // inheritanceScopeAdapter keeps relationship execution independent from the // container that owns the records. Adapters are private preparation machinery; // public loading continues to use mapping scopes until record inheritance is // activated separately. type inheritanceScopeAdapter interface { scope() *document.Node scopePath() Path collectionPolicies() collectionPolicyScope applicationPolicy() sequencePolicy templates() []inheritanceExecutionTemplate slots() []inheritanceScopeSlot slotOverhead() int rebuild(resolved []*document.Node) *document.Node } type inheritanceExecutionTemplate struct { entry document.Entry path Path collectionPolicies collectionPolicyScope parent int hasParent bool hasDirective bool cancelled bool directive document.Entry directivePath Path identityFields []string } type inheritanceScopeSlot struct { value *document.Node path Path template int hasTemplate bool } type mappingInheritanceScope struct{ graph *inheritanceGraph } func (scope mappingInheritanceScope) scope() *document.Node { return scope.graph.scope } func (scope mappingInheritanceScope) scopePath() Path { return scope.graph.scopePath } func (scope mappingInheritanceScope) collectionPolicies() collectionPolicyScope { return scope.graph.collectionPolicies } func (scope mappingInheritanceScope) applicationPolicy() sequencePolicy { return scope.graph.applicationPolicy } func (scope mappingInheritanceScope) slotOverhead() int { return 1 } func (scope mappingInheritanceScope) templates() []inheritanceExecutionTemplate { result := make([]inheritanceExecutionTemplate, len(scope.graph.nodes)) for index, node := range scope.graph.nodes { var directive document.Entry if node.hasDirective { directive = node.directive } result[index] = inheritanceExecutionTemplate{ entry: node.entry, path: node.path, collectionPolicies: node.collectionPolicies, parent: node.parent, hasParent: node.hasParent, hasDirective: node.hasDirective, cancelled: node.cancelled, directive: directive, directivePath: node.path.Key(inheritanceKey), } } return result } func (scope mappingInheritanceScope) slots() []inheritanceScopeSlot { result := make([]inheritanceScopeSlot, len(scope.graph.scope.Mapping)) for index, entry := range scope.graph.scope.Mapping { slot := inheritanceScopeSlot{ value: entry.Value, path: scope.graph.scopePath.Key(entry.Key), } if template, ok := scope.graph.byName[entry.Key]; ok { slot.template, slot.hasTemplate = template, true } result[index] = slot } return result } func (scope mappingInheritanceScope) rebuild(resolved []*document.Node) *document.Node { root := *scope.graph.scope root.Mapping = make([]document.Entry, len(scope.graph.scope.Mapping)) for index, entry := range scope.graph.scope.Mapping { root.Mapping[index] = entry if template, ok := scope.graph.byName[entry.Key]; ok { root.Mapping[index].Value = resolved[template] } else { root.Mapping[index].Value = document.Clone(entry.Value) } } return &root } type identifiedSequenceInheritanceScope struct { graph *identifiedInheritanceGraph policies collectionPolicyScope application sequencePolicy } func (scope identifiedSequenceInheritanceScope) scope() *document.Node { return scope.graph.scope } func (scope identifiedSequenceInheritanceScope) scopePath() Path { return scope.graph.scopePath } func (scope identifiedSequenceInheritanceScope) collectionPolicies() collectionPolicyScope { return scope.policies } func (scope identifiedSequenceInheritanceScope) applicationPolicy() sequencePolicy { return scope.application } func (scope identifiedSequenceInheritanceScope) slotOverhead() int { return 0 } func (scope identifiedSequenceInheritanceScope) templates() []inheritanceExecutionTemplate { result := make([]inheritanceExecutionTemplate, len(scope.graph.nodes)) for index, node := range scope.graph.nodes { result[index] = inheritanceExecutionTemplate{ entry: document.Entry{Value: node.item}, path: node.path, collectionPolicies: inheritancePolicyScopeAtPath(scope.policies, node.path), parent: node.parent, hasParent: node.hasParent, hasDirective: node.directive != nil, cancelled: node.cancelled, directive: document.Entry{Value: node.directive}, directivePath: node.directivePath, identityFields: scope.graph.fields, } } return result } func (scope identifiedSequenceInheritanceScope) slots() []inheritanceScopeSlot { result := make([]inheritanceScopeSlot, len(scope.graph.nodes)) for index, node := range scope.graph.nodes { result[index] = inheritanceScopeSlot{ value: node.item, path: node.path, template: index, hasTemplate: true, } } return result } func (scope identifiedSequenceInheritanceScope) rebuild(resolved []*document.Node) *document.Node { root := *scope.graph.scope root.Sequence = make([]*document.Node, len(scope.graph.nodes)) for index := range root.Sequence { root.Sequence[index] = resolved[index] } return &root } func preserveInheritanceIdentityFields(merged, child *document.Node, fields []string) { if len(fields) == 0 || merged == nil || merged.Kind != document.Mapping || child == nil || child.Kind != document.Mapping { return } for _, field := range fields { for _, entry := range child.Mapping { if entry.Key != field { continue } for index := range merged.Mapping { if merged.Mapping[index].Key == field { merged.Mapping[index] = document.Entry{Key: field, Value: document.Clone(entry.Value)} break } } break } } } func inheritanceParentPayload(parent *document.Node, identityFields []string) *document.Node { cloned := document.Clone(parent) if len(identityFields) == 0 || cloned == nil || cloned.Kind != document.Mapping { return cloned } excluded := make(map[string]struct{}, len(identityFields)) for _, field := range identityFields { excluded[field] = struct{}{} } kept := cloned.Mapping[:0] for _, entry := range cloned.Mapping { if _, omit := excluded[entry.Key]; !omit { kept = append(kept, entry) } } cloned.Mapping = kept return cloned } func applyIdentifiedInheritanceTemplates( graph *identifiedInheritanceGraph, limits projectionLimits, inputOrigins map[string]Origin, policies collectionPolicyScope, application sequencePolicy, budget ...*retainedNodeCounter, ) (*inheritanceScope, []Diagnostic) { order, diagnostics := graph.schedule() if len(diagnostics) != 0 { return nil, diagnostics } adapter := identifiedSequenceInheritanceScope{ graph: graph, policies: policies, application: application, } return applyInheritanceScopeTemplates(adapter, order, limits, inputOrigins, budget...) }