Prepare configuration scope metadata

This commit is contained in:
2026-10-09 01:46:19 +00:00
parent a7960e5e94
commit 62b4a48e4f
8 changed files with 242 additions and 105 deletions

109
config.go
View File

@@ -97,6 +97,14 @@ type scopesInventory struct {
Scopes []configScopeInfo `yaml:"scopes"`
}
type preparedConfigSuite[C any] struct {
suite ConfigSuite[C]
ordered []ConfigScope[C]
byName map[string]ConfigScope[C]
installed []ConfigOperation
inventory scopesInventory
}
type configScopesInput struct {
Format string
}
@@ -135,42 +143,27 @@ func RegisterConfig[C any](app *App, suite ConfigSuite[C]) error {
if _, exists := app.commands["config"]; exists {
return fmt.Errorf("command path %q is already registered", "config")
}
prepared := prepareConfigSuite(normalized)
commands := make([]Command, 0, 5)
installed := make([]ConfigOperation, 0, 4)
if configOperationDeclared(normalized.Scopes, ConfigValidate) {
validate, err := newConfigValidateCommand(normalized)
for _, operation := range prepared.installed {
var command Command
switch operation {
case ConfigValidate:
command, err = newConfigValidateCommand(prepared)
case ConfigShow:
command, err = newConfigShowCommand(prepared)
case ConfigSources:
command, err = newConfigSourcesCommand(prepared)
case ConfigExplain:
command, err = newConfigExplainCommand(prepared)
}
if err != nil {
return err
}
commands = append(commands, validate)
installed = append(installed, ConfigValidate)
commands = append(commands, command)
}
if configOperationDeclared(normalized.Scopes, ConfigShow) {
show, err := newConfigShowCommand(normalized)
if err != nil {
return err
}
commands = append(commands, show)
installed = append(installed, ConfigShow)
}
if configOperationDeclared(normalized.Scopes, ConfigSources) {
sources, err := newConfigSourcesCommand(normalized)
if err != nil {
return err
}
commands = append(commands, sources)
installed = append(installed, ConfigSources)
}
if configOperationDeclared(normalized.Scopes, ConfigExplain) {
explain, err := newConfigExplainCommand(normalized)
if err != nil {
return err
}
commands = append(commands, explain)
installed = append(installed, ConfigExplain)
}
command, err := newConfigScopesCommandWithScopes(normalized.Scopes, installed)
command, err := newConfigScopesCommandWithPrepared(prepared)
if err != nil {
return err
}
@@ -290,6 +283,8 @@ func normalizeConfigSuite[C any](suite ConfigSuite[C]) (ConfigSuite[C], error) {
scope.Operations = operations
normalized.Scopes = append(normalized.Scopes, scope)
}
seenNames := make(map[string]struct{}, len(normalized.Selectors))
seenShorts := make(map[string]struct{}, len(normalized.Selectors))
for index := range normalized.Selectors {
selector := &normalized.Selectors[index]
binding := selector.Binding.binding
@@ -321,16 +316,48 @@ func normalizeConfigSuite[C any](suite ConfigSuite[C]) (ConfigSuite[C], error) {
unique = append(unique, scopeName)
}
selector.Scopes = append([]string(nil), unique...)
for previous := 0; previous < index; previous++ {
other := normalized.Selectors[previous].Binding.binding.meta
if other.Name == binding.meta.Name || binding.meta.Short != "" && other.Short == binding.meta.Short {
if _, exists := seenNames[binding.meta.Name]; exists {
return ConfigSuite[C]{}, fmt.Errorf("duplicate configuration selector flag --%s", binding.meta.Name)
}
if binding.meta.Short != "" {
if _, exists := seenShorts[binding.meta.Short]; exists {
return ConfigSuite[C]{}, fmt.Errorf("duplicate configuration selector flag --%s", binding.meta.Name)
}
seenShorts[binding.meta.Short] = struct{}{}
}
seenNames[binding.meta.Name] = struct{}{}
}
return normalized, nil
}
func prepareConfigSuite[C any](suite ConfigSuite[C]) *preparedConfigSuite[C] {
prepared := &preparedConfigSuite[C]{
suite: suite,
ordered: orderedConfigScopes(suite.Scopes),
byName: make(map[string]ConfigScope[C], len(suite.Scopes)),
installed: make([]ConfigOperation, 0, 4),
inventory: scopesInventory{Scopes: make([]configScopeInfo, 0, len(suite.Scopes))},
}
for _, scope := range suite.Scopes {
prepared.byName[scope.Name] = scope
}
for _, operation := range []ConfigOperation{ConfigValidate, ConfigShow, ConfigSources, ConfigExplain} {
if configOperationDeclared(suite.Scopes, operation) {
prepared.installed = append(prepared.installed, operation)
}
}
for _, scope := range prepared.ordered {
operations := make([]ConfigOperation, 0, len(scope.Operations))
for _, operation := range scope.Operations {
if containsOperation(prepared.installed, operation) {
operations = append(operations, operation)
}
}
prepared.inventory.Scopes = append(prepared.inventory.Scopes, configScopeInfo{Name: scope.Name, Operations: operations})
}
return prepared
}
func normalizeConfigOperations(operations []ConfigOperation) ([]ConfigOperation, error) {
seen := make(map[ConfigOperation]struct{}, len(operations))
for _, operation := range operations {
@@ -350,7 +377,7 @@ func normalizeConfigOperations(operations []ConfigOperation) ([]ConfigOperation,
return ordered, nil
}
func newConfigScopesCommandWithScopes[C any](scopes []ConfigScope[C], installed []ConfigOperation) (Command, error) {
func newConfigScopesCommandWithPrepared[C any](prepared *preparedConfigSuite[C]) (Command, error) {
formatDefault := "text"
spec := CommandSpec[configScopesInput]{
Name: "scopes",
@@ -362,22 +389,12 @@ func newConfigScopesCommandWithScopes[C any](scopes []ConfigScope[C], installed
})},
CheckInput: func(input configScopesInput) error { return checkConfigFormat(input.Format) },
Run: func(invocation Invocation, input configScopesInput) Outcome {
inventory := scopesInventory{Scopes: make([]configScopeInfo, 0, len(scopes))}
for _, scope := range orderedConfigScopes(scopes) {
operations := make([]ConfigOperation, 0, len(scope.Operations))
for _, operation := range scope.Operations {
if containsOperation(installed, operation) {
operations = append(operations, operation)
}
}
inventory.Scopes = append(inventory.Scopes, configScopeInfo{Name: scope.Name, Operations: operations})
}
var output []byte
var err error
if input.Format == "yaml" {
output, err = yaml.Marshal(inventory)
output, err = yaml.Marshal(prepared.inventory)
} else {
output = []byte(formatScopeInventory(inventory))
output = []byte(formatScopeInventory(prepared.inventory))
}
if err != nil {
return Failure(err)