Add ConfigKit comparison acquisition

This commit is contained in:
2026-10-11 21:47:16 +00:00
parent d2434b9580
commit 3542422b2f
4 changed files with 408 additions and 14 deletions

View File

@@ -11,7 +11,7 @@ import (
configkit "gitea.maximumdirect.net/eric/ConfigKit"
)
// FromConfigKit adapts a typed ConfigKit loader to all four native
// FromConfigKit adapts a typed ConfigKit loader to all five native
// configuration operations. The loader receives the operation's explicit
// Selection, initialized input, and shared invocation state, and runs only when
// an installed operation selects this scope. Construction and help do not call
@@ -24,7 +24,7 @@ func FromConfigKit[I, S, T any](
scope := Scope[I, S]{
Name: name,
Summary: summary,
Operations: []Operation{Validate, Show, Sources, Explain},
Operations: []Operation{Validate, Show, Sources, Explain, Diff},
}
if load == nil {
return WithScopeError(scope, fmt.Errorf("ConfigKit scope %q loader must not be nil", name))
@@ -49,10 +49,71 @@ func FromConfigKit[I, S, T any](
Diagnostics: configKitDiagnostics(name, sources, result.Diagnostics()),
}, nil
}
scope.Compare = func(ctx context.Context, request ComparisonRequest[I], state S) (commandkit.Acquired[[]configkit.Difference], error) {
if err := ctx.Err(); err != nil {
return commandkit.Acquired[[]configkit.Difference]{}, fmt.Errorf("ConfigKit scope %q comparison before left load: %w", name, err)
}
left, leftDiagnostics, err := loadComparisonSide(ctx, name, "left", load, request.Left, state)
diagnostics := append([]commandkit.Diagnostic(nil), leftDiagnostics...)
if err != nil {
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: diagnostics}, err
}
if err := ctx.Err(); err != nil {
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: diagnostics}, fmt.Errorf("ConfigKit scope %q comparison after left load: %w", name, err)
}
right, rightDiagnostics, err := loadComparisonSide(ctx, name, "right", load, request.Right, state)
diagnostics = append(diagnostics, rightDiagnostics...)
if err != nil {
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: diagnostics}, err
}
if err := ctx.Err(); err != nil {
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: diagnostics}, fmt.Errorf("ConfigKit scope %q comparison before native diff: %w", name, err)
}
differences, err := configkit.Diff(left, right)
if err != nil {
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: diagnostics}, fmt.Errorf("ConfigKit scope %q native diff: %w", name, err)
}
return commandkit.Acquired[[]configkit.Difference]{Value: differences, Diagnostics: diagnostics}, nil
}
return scope
}
func loadComparisonSide[I, S, T any](
ctx context.Context,
scope, side string,
load func(context.Context, Selection, I, S) (*configkit.Result[T], error),
request ComparisonSide[I],
state S,
) (*configkit.Result[T], []commandkit.Diagnostic, error) {
result, err := load(ctx, request.Selection, request.Input, state)
if err != nil {
var loadError *configkit.LoadError
var diagnostics []commandkit.Diagnostic
if errors.As(err, &loadError) && loadError != nil {
diagnostics = configKitComparisonDiagnostics(scope, side, loadError.Sources(), loadError.Diagnostics())
}
return nil, diagnostics, fmt.Errorf("ConfigKit scope %q %s load: %w", scope, side, err)
}
if result == nil {
return nil, nil, fmt.Errorf("ConfigKit scope %q %s loader returned a nil result", scope, side)
}
return result, configKitComparisonDiagnostics(scope, side, result.Sources(), result.Diagnostics()), nil
}
func configKitDiagnostics(scope string, sources []configkit.SourceInfo, records []configkit.Diagnostic) []commandkit.Diagnostic {
return configKitDiagnosticsWithHeading("Configuration scope "+strconv.Quote(scope)+":\n", sources, records)
}
func configKitComparisonDiagnostics(scope, side string, sources []configkit.SourceInfo, records []configkit.Diagnostic) []commandkit.Diagnostic {
return configKitDiagnosticsWithHeading("Configuration scope "+strconv.Quote(scope)+" "+side+":\n", sources, records)
}
func configKitDiagnosticsWithHeading(heading string, sources []configkit.SourceInfo, records []configkit.Diagnostic) []commandkit.Diagnostic {
if len(records) == 0 {
return nil
}
@@ -72,7 +133,7 @@ func configKitDiagnostics(scope string, sources []configkit.SourceInfo, records
return []commandkit.Diagnostic{{
Severity: severity,
Write: func(writer io.Writer) error {
if err := writeDiagnosticText(writer, "Configuration scope "+strconv.Quote(scope)+":\n"); err != nil {
if err := writeDiagnosticText(writer, heading); err != nil {
return err
}
if len(sources) > 0 {