Files
ConfigKit/internal/engine/inheritance_scope.go

199 lines
6.8 KiB
Go

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))
policyIndex := newInheritancePolicyIndex(scope.policies)
for index, node := range scope.graph.nodes {
result[index] = inheritanceExecutionTemplate{
entry: document.Entry{Value: node.item}, path: node.path,
collectionPolicies: policyIndex.localize(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...)
}