102 lines
3.1 KiB
Go
102 lines
3.1 KiB
Go
package ckconfig
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
|
|
commandkit "gitea.maximumdirect.net/eric/CommandKit"
|
|
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
|
)
|
|
|
|
// FromConfigKit adapts a typed ConfigKit loader to all four native
|
|
// configuration operations. It does not invoke load during construction. A
|
|
// nil original loader is retained as a construction issue, even if callers
|
|
// later narrow the scope's operation list.
|
|
func FromConfigKit[I, S, T any](
|
|
name, summary string,
|
|
load func(context.Context, Selection, I, S) (*configkit.Result[T], error),
|
|
) Scope[I, S] {
|
|
scope := Scope[I, S]{
|
|
Name: name,
|
|
Summary: summary,
|
|
Operations: []Operation{Validate, Show, Sources, Explain},
|
|
}
|
|
if load == nil {
|
|
return WithScopeError(scope, fmt.Errorf("ConfigKit scope %q loader must not be nil", name))
|
|
}
|
|
|
|
scope.Acquire = func(ctx context.Context, request Request, input I, state S) (commandkit.Acquired[View], error) {
|
|
result, err := load(ctx, request.Selection, input, state)
|
|
if err != nil {
|
|
var loadError *configkit.LoadError
|
|
if errors.As(err, &loadError) && loadError != nil {
|
|
return commandkit.Acquired[View]{Diagnostics: configKitDiagnostics(name, loadError.Sources(), loadError.Diagnostics())}, err
|
|
}
|
|
return commandkit.Acquired[View]{}, err
|
|
}
|
|
if result == nil {
|
|
return commandkit.Acquired[View]{}, fmt.Errorf("ConfigKit scope %q returned a nil result", name)
|
|
}
|
|
|
|
sources := result.Sources()
|
|
return commandkit.Acquired[View]{
|
|
Value: View{Effective: result, Sources: result, Explanation: result},
|
|
Diagnostics: configKitDiagnostics(name, sources, result.Diagnostics()),
|
|
}, nil
|
|
}
|
|
return scope
|
|
}
|
|
|
|
func configKitDiagnostics(scope string, sources []configkit.SourceInfo, records []configkit.Diagnostic) []commandkit.Diagnostic {
|
|
if len(records) == 0 {
|
|
return nil
|
|
}
|
|
|
|
severity := commandkit.SeverityWarning
|
|
for _, record := range records {
|
|
if record.Severity == configkit.SeverityError {
|
|
severity = commandkit.SeverityError
|
|
break
|
|
}
|
|
}
|
|
|
|
// Capture the upstream snapshots so later calls to Write do not depend on
|
|
// slices returned by the provider.
|
|
sources = append([]configkit.SourceInfo(nil), sources...)
|
|
records = append([]configkit.Diagnostic(nil), records...)
|
|
return []commandkit.Diagnostic{{
|
|
Severity: severity,
|
|
Write: func(writer io.Writer) error {
|
|
if err := writeDiagnosticText(writer, "Configuration scope "+strconv.Quote(scope)+":\n"); err != nil {
|
|
return err
|
|
}
|
|
if len(sources) > 0 {
|
|
if err := writeDiagnosticText(writer, "Source IDs:\n"); err != nil {
|
|
return err
|
|
}
|
|
for _, source := range sources {
|
|
line := " " + strconv.Quote(source.ID) + ": label=" + strconv.Quote(source.Label) + ", path=" + strconv.Quote(source.Path) + "\n"
|
|
if err := writeDiagnosticText(writer, line); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return configkit.WriteDiagnostics(writer, records)
|
|
},
|
|
}}
|
|
}
|
|
|
|
func writeDiagnosticText(writer io.Writer, value string) error {
|
|
written, err := io.WriteString(writer, value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if written != len(value) {
|
|
return io.ErrShortWrite
|
|
}
|
|
return nil
|
|
}
|