Add selected profile overlays

This commit is contained in:
2026-09-06 21:03:45 +00:00
parent 71a73bcfd8
commit ef0e6078f7
17 changed files with 897 additions and 81 deletions

42
load.go
View File

@@ -17,8 +17,8 @@ type Definition[T any] struct {
}
// Options contains the application definition and per-load input selections.
// Imports enables explicit path and glob imports; later precedence layers remain
// unavailable until their owning features are implemented.
// Imports enables explicit path and glob imports; Profile selects one fixed profile
// overlay. Explicit overrides remain unavailable until implemented.
type Options[T any] struct {
Definition Definition[T]
Imports bool
@@ -94,9 +94,9 @@ func (r *Result[T]) Diagnostics() []Diagnostic {
return cloneDiagnostics(r.diagnostics)
}
// Load parses and strictly decodes zero or one selected source plus explicitly
// enabled imports. It accepts at most one Options value and returns no result when
// any error is reported.
// Load parses and strictly decodes zero or one selected source, explicitly enabled
// imports, and one selected profile. It accepts at most one Options value and
// returns no result when any error is reported.
func Load[T any](source Source, supplied ...Options[T]) (*Result[T], error) {
accumulator := diagnosticAccumulator{}
options, diagnostics := selectOptions(source, supplied)
@@ -127,7 +127,7 @@ func Load[T any](source Source, supplied ...Options[T]) (*Result[T], error) {
if parsed.source != nil {
accumulator.sources = append(accumulator.sources, *parsed.source)
}
if options.Filesystem != nil {
if options.Filesystem != nil && !activeProfile(options.Profile) {
if diagnostic := validateFilesystemContext(source, options.Filesystem, parsed); diagnostic != nil {
accumulator.add(*diagnostic)
return nil, accumulator.loadError()
@@ -153,6 +153,26 @@ func Load[T any](source Source, supplied ...Options[T]) (*Result[T], error) {
if hasErrors(versionDiagnostics) {
return nil, accumulator.loadError()
}
if activeProfile(options.Profile) {
if composition == nil {
composition, diagnostics = composeDocuments(inputDocument)
if len(diagnostics) != 0 {
accumulator.add(diagnostics...)
return nil, accumulator.loadError()
}
composition.inputBytes = parsed.bytes
composition.inputNodes = countInputNodes(parsed.document)
}
composition, accumulator.sources, diagnostics = loadProfileDocument(
source, options.Filesystem, parsed, composition, accumulator.sources,
options.Profile, options.Definition.Version, defaultImportLimits(),
)
if len(diagnostics) != 0 {
accumulator.add(diagnostics...)
return nil, accumulator.loadError()
}
inputDocument = composition.root
}
defaults := reflect.Zero(reflect.TypeFor[T]())
if options.Definition.Defaults != nil {
@@ -171,6 +191,9 @@ func Load[T any](source Source, supplied ...Options[T]) (*Result[T], error) {
configuration := decoded.value.Interface().(T)
metadata := metadataFromDecoded(decoded)
if composition != nil {
applyOverlayMetadata(&metadata, composition.overlays)
}
final := decoded.final
var resolutionSnapshots []resolutionSnapshot
if options.Definition.Resolve != nil {
@@ -213,8 +236,8 @@ func selectOptions[T any](source Source, supplied []Options[T]) (Options[T], []D
if definition.Secret != nil {
diagnostics = append(diagnostics, optionDiagnostic(CodeOptionsUnavailable, "dynamic secret classification is not available in this release boundary"))
}
if options.Profile != "" && options.Profile != "default" {
diagnostics = append(diagnostics, optionDiagnostic(CodeOptionsUnavailable, "configuration profiles are not available in this release boundary"))
if !validProfileName(options.Profile) {
diagnostics = append(diagnostics, optionDiagnostic(CodeOptionsInvalid, "profile name must contain only ASCII letters, digits, underscore, or hyphen"))
}
if options.Filesystem != nil {
diagnostics = append(diagnostics, filesystemOptionDiagnostics(source, options.Filesystem)...)
@@ -228,6 +251,9 @@ func selectOptions[T any](source Source, supplied []Options[T]) (Options[T], []D
if source.kind == sourceNone && options.Profile != "" && options.Profile != "default" {
diagnostics = append(diagnostics, optionDiagnostic(CodeOptionsInvalid, "NoSource cannot select an active profile"))
}
if options.Filesystem == nil && (source.kind == sourceBytes || source.kind == sourceReader) && (options.Imports || activeProfile(options.Profile)) {
diagnostics = append(diagnostics, optionDiagnostic(CodeOptionsInvalid, "byte and reader imports or profiles require filesystem context"))
}
if definition.Version != nil {
if !definition.Version.Path.valid() {
diagnostics = append(diagnostics, optionDiagnostic(CodeOptionsInvalid, "schema-version path is invalid"))