2026-10-19 20:02:05 +00:00
package engine
import (
"path"
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
)
type importSelector uint8
const (
importSelectorPath importSelector = iota + 1
importSelectorDirectory
importSelectorGlob
)
type importFieldSite struct {
path Path
location Location
}
// importDeclarationSpec preserves authored presence until discovery policy is resolved.
type importDeclarationSpec struct {
2026-10-19 20:13:33 +00:00
path Path
location Location
selector importSelector
paths [ ] string
selectorSites [ ] importFieldSite
glob string
globSegments [ ] string
required * bool
recursive * bool
follow * bool
hidden * bool
pattern [ ] string
patternSet bool
exclude [ ] string
excludeSet bool
depth int
depthSet bool
destination Path
placement placementMode
fieldSites map [ string ] importFieldSite
2026-10-19 20:02:05 +00:00
}
// importDefaults are the engine's private copy of load-wide discovery defaults.
type importDefaults struct {
required * bool
follow * bool
hidden * bool
recursive * bool
pattern [ ] string
}
2026-10-19 21:06:31 +00:00
// ImportDefaults transports root-owned load defaults across the facade
// boundary. Load options snapshot this value before source acquisition.
type ImportDefaults struct {
// Required controls absence handling when non-nil.
Required * bool
// Follow controls symlink following when non-nil.
Follow * bool
// Hidden controls hidden-entry discovery when non-nil.
Hidden * bool
// Recursive controls descendant directory traversal when non-nil.
Recursive * bool
// Pattern filters directory-search filenames; nil and empty differ.
Pattern [ ] string
}
// OptionsWithImportDefaults copies root-owned defaults into ordinary options.
func OptionsWithImportDefaults [ T any ] ( options Options [ T ] , value ImportDefaults ) Options [ T ] {
options . importDefaults = privateImportDefaults ( value )
return options
}
// LayeredOptionsWithImportDefaults copies root-owned defaults into grouped options.
func LayeredOptionsWithImportDefaults [ T any ] ( options LayeredOptions [ T ] , value ImportDefaults ) LayeredOptions [ T ] {
options . importDefaults = privateImportDefaults ( value )
return options
}
func privateImportDefaults ( value ImportDefaults ) importDefaults {
return snapshotImportDefaults ( importDefaults {
required : value . Required , follow : value . Follow , hidden : value . Hidden ,
recursive : value . Recursive , pattern : value . Pattern ,
} )
}
2026-10-19 20:02:05 +00:00
type resolvedImportPolicy struct {
required bool
follow bool
hidden bool
recursive bool
pattern [ ] string
depth int
depthSet bool
exclude [ ] string
destination Path
placement placementMode
}
func snapshotImportDefaults ( value importDefaults ) importDefaults {
return importDefaults {
required : cloneBool ( value . required ) ,
follow : cloneBool ( value . follow ) ,
hidden : cloneBool ( value . hidden ) ,
recursive : cloneBool ( value . recursive ) ,
pattern : cloneStrings ( value . pattern ) ,
}
}
func cloneBool ( value * bool ) * bool {
if value == nil {
return nil
}
cloned := * value
return & cloned
}
func cloneStrings ( values [ ] string ) [ ] string {
if values == nil {
return nil
}
return append ( make ( [ ] string , 0 , len ( values ) ) , values ... )
}
func validateImportDefaults ( value importDefaults ) [ ] Diagnostic {
for _ , pattern := range value . pattern {
if _ , err := path . Match ( pattern , "" ) ; err != nil {
return [ ] Diagnostic { optionDiagnostic ( CodeOptionsInvalid , "import default pattern is invalid" ) }
}
}
return nil
}
func resolveImportPolicy ( spec importDeclarationSpec , defaults importDefaults ) ( resolvedImportPolicy , [ ] Diagnostic ) {
defaults = snapshotImportDefaults ( defaults )
policy := resolvedImportPolicy {
required : boolOr ( spec . required , defaults . required , false ) ,
follow : true ,
hidden : true ,
recursive : false ,
destination : spec . destination . clone ( ) ,
placement : spec . placement ,
depth : spec . depth ,
depthSet : spec . depthSet ,
exclude : cloneStrings ( spec . exclude ) ,
}
if spec . selector == importSelectorDirectory || spec . selector == importSelectorGlob {
policy . follow = boolOr ( spec . follow , defaults . follow , true )
policy . hidden = boolOr ( spec . hidden , defaults . hidden , true )
}
if spec . selector == importSelectorDirectory {
policy . recursive = boolOr ( spec . recursive , defaults . recursive , false )
if spec . patternSet {
policy . pattern = cloneStrings ( spec . pattern )
} else {
policy . pattern = cloneStrings ( defaults . pattern )
}
}
if spec . depthSet && ! policy . recursive {
site := spec . fieldSites [ "depth" ]
location := site . location
return resolvedImportPolicy { } , [ ] Diagnostic { {
Code : CodeImportInvalid , Severity : SeverityError ,
Path : site . path . clone ( ) , Message : "depth requires effective recursive discovery" ,
Primary : & location ,
} }
}
return policy , nil
}
func boolOr ( explicit , application * bool , fallback bool ) bool {
if explicit != nil {
return * explicit
}
if application != nil {
return * application
}
return fallback
}
// parseImportSpecificationDirective validates every declaration before callers select candidates.
func parseImportSpecificationDirective ( node * document . Node , directivePath Path ) ( [ ] importDeclarationSpec , [ ] Diagnostic ) {
if node . Kind != document . Mapping {
return nil , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , directivePath , "configuration directive must be a mapping" , node ) }
}
var specs [ ] importDeclarationSpec
var diagnostics [ ] Diagnostic
for _ , entry := range node . Mapping {
fieldPath := directivePath . Key ( entry . Key )
if entry . Key != "imports" {
diagnostics = append ( diagnostics , nodeImportDiagnostic ( CodeImportInvalid , fieldPath , "configuration directive field is not recognized" , entry . Value ) )
continue
}
2026-10-23 14:01:39 +00:00
parsed , issues := parseImportDeclarations ( entry . Value , fieldPath )
specs = append ( specs , parsed ... )
diagnostics = append ( diagnostics , issues ... )
}
return specs , canonicalDiagnostics ( diagnostics )
}
func parseImportDeclarations ( node * document . Node , declarationPath Path ) ( [ ] importDeclarationSpec , [ ] Diagnostic ) {
if node == nil || node . Kind != document . Sequence {
return nil , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , declarationPath , "imports must be a sequence" , node ) }
}
var specs [ ] importDeclarationSpec
var diagnostics [ ] Diagnostic
for index , declarationNode := range node . Sequence {
spec , issues := parseImportDeclarationSpec ( declarationNode , declarationPath . Index ( index ) )
if len ( issues ) != 0 {
diagnostics = append ( diagnostics , issues ... )
2026-10-19 20:02:05 +00:00
continue
}
2026-10-23 14:01:39 +00:00
specs = append ( specs , spec )
2026-10-19 20:02:05 +00:00
}
return specs , canonicalDiagnostics ( diagnostics )
}
func parseImportDeclarationSpec ( node * document . Node , declarationPath Path ) ( importDeclarationSpec , [ ] Diagnostic ) {
if node . Kind != document . Mapping {
return importDeclarationSpec { } , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , declarationPath , "import declaration must be a mapping" , node ) }
}
spec := importDeclarationSpec {
path : declarationPath . clone ( ) ,
location : publicLocation ( node . Location ) ,
placement : placementModeMapping ,
fieldSites : make ( map [ string ] importFieldSite , len ( node . Mapping ) ) ,
}
selectors := 0
selectorIndex := - 1
for index , entry := range node . Mapping {
if selectorForField ( entry . Key ) != 0 {
selectors ++
selectorIndex = index
}
}
if selectors != 1 {
return importDeclarationSpec { } , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , declarationPath , "import declaration requires exactly one path, dir, or glob selector" , node ) }
}
selectorEntry := node . Mapping [ selectorIndex ]
spec . selector = selectorForField ( selectorEntry . Key )
for _ , entry := range node . Mapping {
fieldPath := declarationPath . Key ( entry . Key )
spec . fieldSites [ entry . Key ] = importFieldSite { path : fieldPath . clone ( ) , location : publicLocation ( entry . Value . Location ) }
if selectorForField ( entry . Key ) != 0 {
continue
}
if ! importFieldApplies ( entry . Key , spec . selector ) {
return importDeclarationSpec { } , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , fieldPath , "import declaration field is not supported by this selector" , entry . Value ) }
}
switch entry . Key {
case "required" , "recursive" , "follow" , "hidden" :
if entry . Value . Kind != document . Boolean {
return importDeclarationSpec { } , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , fieldPath , entry . Key + " must be a boolean" , entry . Value ) }
}
value := entry . Value . Text == "true"
switch entry . Key {
case "required" :
spec . required = & value
case "recursive" :
spec . recursive = & value
case "follow" :
spec . follow = & value
case "hidden" :
spec . hidden = & value
}
case "pattern" :
values , origins , ok , badIndex := parseStringSet ( entry . Value )
if ! ok {
return importDeclarationSpec { } , [ ] Diagnostic { stringSetDiagnostic ( entry . Value , fieldPath , "pattern" , badIndex ) }
}
if badIndex , ok := invalidWildcard ( values ) ; ok {
badPath , badNode := listMemberSite ( entry . Value , fieldPath , origins [ badIndex ] )
return importDeclarationSpec { } , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , badPath , "pattern contains an invalid wildcard expression" , badNode ) }
}
spec . pattern , spec . patternSet = values , true
case "exclude" :
values , origins , ok , badIndex := parseStringSet ( entry . Value )
if ! ok {
return importDeclarationSpec { } , [ ] Diagnostic { stringSetDiagnostic ( entry . Value , fieldPath , "exclude" , badIndex ) }
}
if badIndex , ok := invalidWildcard ( values ) ; ok {
badPath , badNode := listMemberSite ( entry . Value , fieldPath , origins [ badIndex ] )
return importDeclarationSpec { } , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , badPath , "exclude contains an invalid wildcard expression" , badNode ) }
}
spec . exclude , spec . excludeSet = values , true
case "depth" :
depth , ok := parseImportDepth ( entry . Value )
if ! ok {
return importDeclarationSpec { } , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , fieldPath , "depth must be a nonnegative integer" , entry . Value ) }
}
spec . depth , spec . depthSet = depth , true
case "destination" :
destination , diagnostic := parseImportDestination ( entry . Value , fieldPath )
if diagnostic != nil {
return importDeclarationSpec { } , [ ] Diagnostic { * diagnostic }
}
spec . destination = destination
case "placement" :
if entry . Value . Kind != document . String {
return importDeclarationSpec { } , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , fieldPath , "placement must be mapping or sequence-item" , entry . Value ) }
}
switch entry . Value . Text {
case "mapping" :
spec . placement = placementModeMapping
case "sequence-item" :
spec . placement = placementModeSequenceItem
default :
return importDeclarationSpec { } , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , fieldPath , "placement must be mapping or sequence-item" , entry . Value ) }
}
default :
return importDeclarationSpec { } , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , fieldPath , "import declaration field is not recognized" , entry . Value ) }
}
}
selectorPath := declarationPath . Key ( selectorEntry . Key )
spec . fieldSites [ selectorEntry . Key ] = importFieldSite { path : selectorPath . clone ( ) , location : publicLocation ( selectorEntry . Value . Location ) }
switch spec . selector {
case importSelectorPath , importSelectorDirectory :
values , origins , ok , badIndex := parseStringSet ( selectorEntry . Value )
if ! ok {
name := selectorEntry . Key
return importDeclarationSpec { } , [ ] Diagnostic { stringSetDiagnostic ( selectorEntry . Value , selectorPath , name , badIndex ) }
}
for index , value := range values {
if ! validImportPath ( value ) {
badPath , badNode := listMemberSite ( selectorEntry . Value , selectorPath , origins [ index ] )
return importDeclarationSpec { } , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , badPath , selectorEntry . Key + " contains an invalid source path" , badNode ) }
}
2026-10-19 20:07:54 +00:00
badPath , badNode := listMemberSite ( selectorEntry . Value , selectorPath , origins [ index ] )
2026-10-19 20:13:33 +00:00
spec . selectorSites = append ( spec . selectorSites , importFieldSite { path : badPath . clone ( ) , location : publicLocation ( badNode . Location ) } )
2026-10-19 20:02:05 +00:00
}
spec . paths = values
case importSelectorGlob :
if selectorEntry . Value . Kind != document . String || ! validImportPath ( selectorEntry . Value . Text ) {
return importDeclarationSpec { } , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , selectorPath , "glob must be a valid nonempty path pattern string" , selectorEntry . Value ) }
}
segments , ok := validGlobPattern ( selectorEntry . Value . Text )
if ! ok {
return importDeclarationSpec { } , [ ] Diagnostic { nodeImportDiagnostic ( CodeImportInvalid , selectorPath , "glob pattern is invalid" , selectorEntry . Value ) }
}
spec . glob , spec . globSegments = selectorEntry . Value . Text , segments
}
return spec , nil
}
func selectorForField ( field string ) importSelector {
switch field {
case "path" :
return importSelectorPath
case "dir" :
return importSelectorDirectory
case "glob" :
return importSelectorGlob
default :
return 0
}
}
func importFieldApplies ( field string , selector importSelector ) bool {
switch field {
case "required" , "exclude" , "destination" , "placement" :
return true
case "pattern" , "recursive" , "depth" :
return selector == importSelectorDirectory
case "follow" , "hidden" :
return selector == importSelectorDirectory || selector == importSelectorGlob
default :
return false
}
}
func parseStringSet ( node * document . Node ) ( [ ] string , [ ] int , bool , int ) {
if node . Kind == document . String {
return [ ] string { node . Text } , [ ] int { - 1 } , true , - 1
}
if node . Kind != document . Sequence {
return nil , nil , false , - 1
}
values := make ( [ ] string , 0 , len ( node . Sequence ) )
origins := make ( [ ] int , 0 , len ( node . Sequence ) )
seen := make ( map [ string ] struct { } , len ( node . Sequence ) )
for index , item := range node . Sequence {
if item . Kind != document . String {
return nil , nil , false , index
}
if _ , exists := seen [ item . Text ] ; exists {
continue
}
seen [ item . Text ] = struct { } { }
values = append ( values , item . Text )
origins = append ( origins , index )
}
return values , origins , true , - 1
}
func stringSetDiagnostic ( node * document . Node , fieldPath Path , field string , badIndex int ) Diagnostic {
if badIndex >= 0 {
badPath , item := listMemberSite ( node , fieldPath , badIndex )
return nodeImportDiagnostic ( CodeImportInvalid , badPath , field + " members must be strings" , item )
}
return nodeImportDiagnostic ( CodeImportInvalid , fieldPath , field + " must be a string or a flat list of strings" , node )
}
func invalidWildcard ( values [ ] string ) ( int , bool ) {
for index , value := range values {
if _ , err := path . Match ( value , "" ) ; err != nil {
return index , true
}
}
return - 1 , false
}
func listMemberSite ( node * document . Node , fieldPath Path , index int ) ( Path , * document . Node ) {
if node . Kind != document . Sequence || index < 0 {
return fieldPath , node
}
return fieldPath . Index ( index ) , node . Sequence [ index ]
}
func parseImportDepth ( node * document . Node ) ( int , bool ) {
if node . Kind != document . Number || node . Text == "" {
return 0 , false
}
if node . Text == "-0" {
return 0 , true
}
var value uint64
maxInt := uint64 ( ^ uint ( 0 ) >> 1 )
for index := 0 ; index < len ( node . Text ) ; index ++ {
character := node . Text [ index ]
if character < '0' || character > '9' {
return 0 , false
}
digit := uint64 ( character - '0' )
if value > ( maxInt - digit ) / 10 {
2026-10-20 00:27:55 +00:00
value = maxInt
continue // Saturate the bound, but validate every remaining character.
2026-10-19 20:02:05 +00:00
}
value = value * 10 + digit
}
return int ( value ) , true
}
func parseImportDestination ( node * document . Node , fieldPath Path ) ( Path , * Diagnostic ) {
if node . Kind != document . Sequence {
diagnostic := nodeImportDiagnostic ( CodeImportInvalid , fieldPath , "destination must be a sequence of strings" , node )
return Path { } , & diagnostic
}
relative := Path { segments : make ( [ ] pathSegment , 0 , len ( node . Sequence ) ) }
for index , item := range node . Sequence {
memberPath := fieldPath . Index ( index )
if item . Kind != document . String {
diagnostic := nodeImportDiagnostic ( CodeImportInvalid , memberPath , "destination members must be strings" , item )
return Path { } , & diagnostic
}
member := Path { segments : [ ] pathSegment { { kind : pathSegmentKey , key : item . Text } } }
if _ , err := normalizeDestination ( member ) ; err != nil {
diagnostic := nodeImportDiagnostic ( CodeImportInvalid , memberPath , "destination members must be non-reserved mapping keys" , item )
return Path { } , & diagnostic
}
relative . segments = append ( relative . segments , pathSegment { kind : pathSegmentKey , key : item . Text } )
}
normalized , err := normalizeDestination ( relative )
if err != nil {
diagnostic := nodeImportDiagnostic ( CodeImportInvalid , fieldPath , "destination must contain only non-reserved mapping keys" , node )
return Path { } , & diagnostic
}
return normalized , nil
}