Files
commandkit/validate.go

119 lines
3.5 KiB
Go
Raw Permalink Normal View History

package commandkit
import (
"bytes"
"fmt"
"strings"
"go.yaml.in/yaml/v4"
)
func configOperationDeclared[C any](scopes []ConfigScope[C], operation ConfigOperation) bool {
for _, scope := range scopes {
if containsOperation(scope.Operations, operation) {
return true
}
}
return false
}
2026-10-09 01:46:19 +00:00
func newConfigValidateCommand[C any](prepared *preparedConfigSuite[C]) (Command, error) {
spec := CommandSpec[configInput[C]]{
Name: string(ConfigValidate),
Summary: "Validate selected configuration scopes.",
2026-10-09 01:46:19 +00:00
Flags: configInputFlags(prepared.suite, "text"),
CheckInput: func(input configInput[C]) error {
2026-10-09 01:46:19 +00:00
_, err := preflightConfig(prepared, input, ConfigValidate)
return err
},
Run: func(invocation Invocation, input configInput[C]) Outcome {
2026-10-09 01:46:19 +00:00
return runConfigValidation(invocation, prepared, input)
},
}
return NewCommand(spec)
}
2026-10-09 01:46:19 +00:00
func runConfigValidation[C any](invocation Invocation, prepared *preparedConfigSuite[C], input configInput[C]) Outcome {
if err := invocation.Context.Err(); err != nil {
return Failure(err)
}
2026-10-09 01:46:19 +00:00
scopeNames, err := selectConfigScopes(prepared, input, ConfigValidate)
if err != nil {
return Failure(err)
}
2026-10-09 01:46:19 +00:00
contexts := contextsForScopes(prepared, input, scopeNames)
diagnostics := make([]Diagnostic, 0)
writtenRecord := false
for _, name := range scopeNames {
2026-10-09 01:46:19 +00:00
scope, ok := findConfigScope(prepared, name)
if !ok || scope.Acquire == nil {
return validationFailure(fmt.Errorf("configuration scope %q has no acquisition callback", name), diagnostics, writtenRecord)
}
if err := invocation.Context.Err(); err != nil {
return validationFailure(err, diagnostics, writtenRecord)
}
acquired, acquireErr := scope.Acquire(invocation.Context, ConfigRequest{Operation: ConfigValidate, Selection: input.Selection}, contexts[name])
diagnostics = append(diagnostics, acquired.Diagnostics...)
if acquireErr != nil {
return validationFailure(acquireErr, diagnostics, writtenRecord)
}
if err := invocation.Context.Err(); err != nil {
return validationFailure(err, diagnostics, writtenRecord)
}
record, err := validationSuccessRecord(name, input.Format)
if err != nil {
return validationFailure(err, diagnostics, writtenRecord)
}
if input.Format == "yaml" && !writtenRecord {
if err := writeReport(invocation.Streams.Out, []byte("scopes:\n")); err != nil {
outcome := validationFailure(err, diagnostics, true)
outcome.DeliveryError = err
return outcome
}
}
if err := writeReport(invocation.Streams.Out, record); err != nil {
outcome := validationFailure(err, diagnostics, true)
outcome.DeliveryError = err
return outcome
}
writtenRecord = true
}
outcome := Success()
outcome.Diagnostics = diagnostics
return outcome
}
func validationFailure(cause error, diagnostics []Diagnostic, reportStarted bool) Outcome {
outcome := Failure(cause)
outcome.Diagnostics = diagnostics
outcome.ReportIncomplete = reportStarted
return outcome
}
func validationSuccessRecord(scope, format string) ([]byte, error) {
if format == "text" {
return []byte(scope + ": valid\n"), nil
}
encoded, err := yaml.Marshal(struct {
Name string `yaml:"name"`
Valid bool `yaml:"valid"`
}{Name: scope, Valid: true})
if err != nil {
return nil, err
}
lines := strings.Split(strings.TrimSuffix(string(encoded), "\n"), "\n")
var record bytes.Buffer
for index, line := range lines {
if index == 0 {
record.WriteString(" - ")
} else {
record.WriteString(" ")
}
record.WriteString(line)
record.WriteByte('\n')
}
return record.Bytes(), nil
}