Files
ConfigKit/layered.go

94 lines
4.1 KiB
Go

package configkit
import "gitea.maximumdirect.net/eric/ConfigKit/internal/engine"
// Input selects one source, its application destination, whether its imports are
// enabled, and any explicit filesystem context. A zero Source is invalid; use
// NoSource() for an omitted main input. Sources keep their documented reuse and
// reader ownership rules.
type Input struct {
Source Source
// Destination establishes the mapping path for Source. Imports inherit this
// path or append a relative destination declared in YAML. Its zero value
// keeps root placement.
Destination Path
// PlacementMode defaults to mapping placement. Sequence-item placement wraps
// the source mapping as one record; unknown values and an empty destination
// fail option validation before acquisition.
PlacementMode PlacementMode
Imports bool
Filesystem *FilesystemContext
}
// Underlay contains inputs that must compose strictly within one tier. Tiers
// appear in lowest-to-highest precedence order; an empty tier is a no-op.
type Underlay struct{ Inputs []Input }
// LayeredOptions selects lower-priority tiers and final application rules.
// An active Overlay requires an explicit OverlayFilesystem; an inactive overlay
// ignores it. Overrides remain explicit CLI records, not general mappings.
// SourceVersionMode applies one declaration policy to main, underlay, import,
// and selected overlay documents. Enabled modes interpret the version path
// source-locally, consume accepted non-main declarations, and protect main's
// destination-prefixed path. The zero value preserves existing behavior; see
// the shared source-version contract in docs/config.md.
type LayeredOptions[T any] struct {
Definition Definition[T]
SourceVersionMode SourceVersionMode
// ImportDefaults supplies one load-wide default policy for enabled main,
// underlay, and transitive declarations. Explicit declaration values win.
ImportDefaults ImportDefaults
Underlays []Underlay
Overlay string
OverlayFilesystem *FilesystemContext
Overrides []Override
}
// LoadLayered acquires the main input first and strictly composes enabled
// imports with each input's tier. It strictly composes contributions within
// each underlay, then recursively combines underlays in listed order beneath
// main, a selected overlay, and CLI overrides. Defaults, decoding, resolution,
// and validation run once after sibling `extends` expansion of the assembled
// input. Source IDs follow acquisition order, not precedence order. Zero or
// one options value is accepted; any error
// returns a nil result.
func LoadLayered[T any](main Input, supplied ...LayeredOptions[T]) (*Result[T], error) {
options := make([]engine.LayeredOptions[T], len(supplied))
for index := range supplied {
options[index] = toEngineLayeredOptions(supplied[index])
}
loaded, err := engine.LoadLayered(toEngineInput(main), secretBinding(), options...)
return fromEngineLoaded(loaded, err)
}
func toEngineInput(input Input) engine.Input {
result := engine.Input{
Source: input.Source.toEngine(), Imports: input.Imports,
Filesystem: toEngineFilesystemContext(input.Filesystem), Destination: toEnginePath(input.Destination),
}
return engine.InputWithPlacement(result, uint8(input.PlacementMode))
}
func toEngineLayeredOptions[T any](options LayeredOptions[T]) engine.LayeredOptions[T] {
result := engine.LayeredOptions[T]{
Definition: toEngineDefinition(options.Definition),
SourceVersionMode: engine.SourceVersionModeFromFacade(uint8(options.SourceVersionMode)),
Overlay: options.Overlay,
OverlayFilesystem: toEngineFilesystemContext(options.OverlayFilesystem),
ImportDefaults: toEngineImportDefaults(options.ImportDefaults),
}
if options.Underlays != nil {
result.Underlays = make([]engine.Underlay, len(options.Underlays))
for index, group := range options.Underlays {
if group.Inputs != nil {
result.Underlays[index].Inputs = make([]engine.Input, len(group.Inputs))
for inputIndex, input := range group.Inputs {
result.Underlays[index].Inputs[inputIndex] = toEngineInput(input)
}
}
}
}
result.Overrides = toEngineOverrides(options.Overrides)
return result
}