2026-10-23 21:11:54 +00:00
package engine
import (
"reflect"
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
)
// decodedOverrideResult keeps the typed pre-CLI projection and raw CLI
// contributions alongside the effective typed value and its inspection data.
type decodedOverrideResult struct {
value reflect . Value
preCLI * document . Node
final * document . Node
presence map [ string ] inputPresence
origins map [ string ] Origin
overrides [ ] preparedOverride
}
// applyDecodedOverrides assigns prepared CLI values to a detached, decoded
// configuration. Each sparse mapping is decoded against the current typed
// value, preserving omitted mapping fields while replacing supplied sequences.
func applyDecodedOverrides (
schema * schemaDescriptor ,
decoded decodedConfiguration ,
batch preparedOverrideBatch ,
context decodeContext ,
) ( decodedOverrideResult , [ ] decodeIssue ) {
if schema == nil || schema . root == nil || ! decoded . value . IsValid ( ) || decoded . value . Type ( ) != schema . root . typeOf || decoded . final == nil {
return decodedOverrideResult { } , [ ] decodeIssue { { code : CodeSchemaUnsupportedType , message : "decoded configuration does not match the schema" } }
}
preCLI := document . Clone ( decoded . final )
working , cloneIssue := cloneConfiguration ( schema , decoded . value , defaultProjectionLimits ( ) )
if cloneIssue != nil {
return decodedOverrideResult { } , [ ] decodeIssue { decodeIssueFromProjection ( cloneIssue ) }
}
presence := cloneInputPresence ( decoded . presence )
origins := cloneOrigins ( decoded . origins )
2026-10-23 23:47:21 +00:00
var lookup * documentLookup
if len ( batch . overrides ) != 0 {
lookup = buildDocumentLookup ( decoded . final )
}
2026-10-23 21:11:54 +00:00
for _ , override := range batch . overrides {
if len ( override . path . segments ) == 0 {
return decodedOverrideResult { } , [ ] decodeIssue { { code : CodeOverrideInvalidPath , message : "configuration override path is invalid" , location : document . Location { SourceID : override . source . ID } } }
}
input , issue := decodedOverrideInput ( override )
if issue != nil {
return decodedOverrideResult { } , [ ] decodeIssue { * issue }
}
updated , issues := decodeValue ( input , working , schema . root , Path { } , schema , context )
if len ( issues ) != 0 {
return decodedOverrideResult { } , issues
}
working = updated
location := document . Location { SourceID : override . source . ID }
recordOverrideAncestors ( presence , origins , override . path , location )
2026-10-23 23:47:21 +00:00
// Prepared paths cannot overlap, so other assignments cannot change
// this target's pre-CLI subtree.
existing , _ := documentAtPathWithLookup ( decoded . final , override . path , lookup )
updateOverridePresence ( presence , existing , override . value , override . path , location )
updateOverrideOrigins ( origins , existing , override . value , override . path , location )
2026-10-23 21:11:54 +00:00
}
final , projectionIssue := projectConfiguration ( schema , working , defaultProjectionLimits ( ) )
if projectionIssue != nil {
return decodedOverrideResult { } , [ ] decodeIssue { decodeIssueFromProjection ( projectionIssue ) }
}
// New fields created by decoding a partial mapping into a previously nil
// pointer have no input origin. Fill those gaps as defaults while retaining
// all surviving source and CLI contributions already in the origin map.
fillDefaultOrigins ( final , Path { } , origins )
return decodedOverrideResult {
value : working ,
preCLI : preCLI ,
final : final ,
presence : presence ,
origins : origins ,
overrides : clonePreparedOverrides ( batch . overrides ) ,
} , nil
}
func recordOverrideAncestors ( presence map [ string ] inputPresence , origins map [ string ] Origin , path Path , location document . Location ) {
mergeOverlayOrigin ( origins , Path { } , location , LayerCLI )
prefix := Path { }
for index , segment := range path . segments {
prefix = prefix . Key ( segment . key )
if index == len ( path . segments ) - 1 {
break
}
pointer := prefix . String ( )
if _ , exists := presence [ pointer ] ; ! exists {
presence [ pointer ] = inputPresence { location : location }
}
mergeOverlayOrigin ( origins , prefix , location , LayerCLI )
}
}
func decodedOverrideInput ( override preparedOverride ) ( * document . Node , * decodeIssue ) {
location := document . Location { SourceID : override . source . ID }
value := document . Clone ( override . value )
if value == nil {
return nil , & decodeIssue { code : CodeOverrideInvalidPath , path : override . path . clone ( ) , message : "configuration override value is invalid" , location : location }
}
for index := len ( override . path . segments ) - 1 ; index >= 0 ; index -- {
segment := override . path . segments [ index ]
if segment . kind == pathSegmentIndex {
return nil , & decodeIssue { code : CodeOverrideInvalidPath , path : override . path . clone ( ) , message : "configuration override path is invalid" , location : location }
}
value = & document . Node {
Kind : document . Mapping ,
Location : location ,
Mapping : [ ] document . Entry { {
Key : segment . key , KeyLocation : location , ValueLocation : location , Value : value ,
} } ,
}
}
return value , nil
}
func cloneInputPresence ( input map [ string ] inputPresence ) map [ string ] inputPresence {
cloned := make ( map [ string ] inputPresence , len ( input ) )
for path , value := range input {
cloned [ path ] = value
}
return cloned
}
func cloneOrigins ( input map [ string ] Origin ) map [ string ] Origin {
cloned := make ( map [ string ] Origin , len ( input ) )
for path , value := range input {
cloned [ path ] = cloneOrigin ( value )
}
return cloned
}
func clonePreparedOverrides ( input [ ] preparedOverride ) [ ] preparedOverride {
cloned := make ( [ ] preparedOverride , len ( input ) )
for index , value := range input {
cloned [ index ] = preparedOverride {
path : value . path . clone ( ) ,
value : document . Clone ( value . value ) ,
source : value . source ,
bytes : value . bytes ,
nodes : value . nodes ,
}
}
return cloned
}
func updateOverridePresence ( presence map [ string ] inputPresence , existing , incoming * document . Node , path Path , location document . Location ) {
if existing != nil && existing . Kind == document . Mapping && incoming . Kind == document . Mapping {
if _ , exists := presence [ path . String ( ) ] ; ! exists {
presence [ path . String ( ) ] = inputPresence { location : location }
}
var index mappingIndex
for _ , entry := range incoming . Mapping {
childPath := path . Key ( entry . Key )
position := index . find ( existing , entry . Key )
var previous * document . Node
if position >= 0 {
previous = existing . Mapping [ position ] . Value
}
updateOverridePresence ( presence , previous , entry . Value , childPath , location )
}
return
}
2026-10-24 14:05:42 +00:00
deletePresencePathSubtree ( presence , existing , path )
2026-10-23 21:11:54 +00:00
collectPresence ( incoming , path , presence )
}
2026-10-24 14:05:42 +00:00
func deletePresencePathSubtree ( presence map [ string ] inputPresence , existing * document . Node , path Path ) {
// The typed pre-CLI tree contains every effective descendant. Visit only
// the replaced subtree rather than searching unrelated metadata entries.
walkDocument ( existing , path , func ( path Path , _ * document . Node ) {
delete ( presence , path . String ( ) )
} )
2026-10-23 21:11:54 +00:00
}
func updateOverrideOrigins ( origins map [ string ] Origin , existing , incoming * document . Node , path Path , location document . Location ) {
if existing != nil && existing . Kind == document . Mapping && incoming . Kind == document . Mapping {
mergeOverlayOrigin ( origins , path , location , LayerCLI )
var index mappingIndex
for _ , entry := range incoming . Mapping {
childPath := path . Key ( entry . Key )
position := index . find ( existing , entry . Key )
var previous * document . Node
if position >= 0 {
previous = existing . Mapping [ position ] . Value
}
updateOverrideOrigins ( origins , previous , entry . Value , childPath , location )
}
return
}
2026-10-24 14:05:42 +00:00
walkDocument ( existing , path , func ( path Path , _ * document . Node ) {
delete ( origins , path . String ( ) )
} )
2026-10-23 21:11:54 +00:00
collectOverlayOrigins ( origins , incoming , path , LayerCLI )
}
func fillDefaultOrigins ( node * document . Node , path Path , origins map [ string ] Origin ) {
if _ , exists := origins [ path . String ( ) ] ; ! exists {
origins [ path . String ( ) ] = Origin { Layer : LayerDefault }
}
switch node . Kind {
case document . Mapping :
for _ , entry := range node . Mapping {
fillDefaultOrigins ( entry . Value , path . Key ( entry . Key ) , origins )
}
case document . Sequence :
for index , child := range node . Sequence {
fillDefaultOrigins ( child , path . Index ( index ) , origins )
}
}
}