Separate ConfigKit messages from explanations
This commit is contained in:
@@ -33,53 +33,58 @@ func FromConfigKit[I, S, T any](
|
||||
|
||||
scope.Acquire = func(ctx context.Context, request Request, input I, state S) (commandkit.Acquired[View], error) {
|
||||
result, err := load(ctx, request.Selection, input, state)
|
||||
acquired := commandkit.Acquired[View]{}
|
||||
if result != nil {
|
||||
acquired.Value = View{Effective: result, Sources: result, Explanation: result}
|
||||
acquired.Diagnostics, acquired.Explanation = configKitDiagnostics(name, result.Sources(), result.Diagnostics())
|
||||
}
|
||||
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
|
||||
warnings, explanation := configKitDiagnostics(name, loadError.Sources(), loadError.Diagnostics())
|
||||
acquired.Diagnostics = append(acquired.Diagnostics, warnings...)
|
||||
acquired.Explanation = appendConfigKitExplanation(acquired.Explanation, explanation)
|
||||
}
|
||||
return commandkit.Acquired[View]{}, err
|
||||
return acquired, 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 acquired, 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...)
|
||||
acquired := commandkit.Acquired[[]configkit.Difference]{}
|
||||
left, leftDiagnostics, leftExplanation, err := loadComparisonSide(ctx, name, "left", load, request.Left, state)
|
||||
acquired.Diagnostics = append(acquired.Diagnostics, leftDiagnostics...)
|
||||
acquired.Explanation = appendConfigKitExplanation(acquired.Explanation, leftExplanation)
|
||||
if err != nil {
|
||||
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: diagnostics}, err
|
||||
return acquired, 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)
|
||||
return acquired, 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...)
|
||||
right, rightDiagnostics, rightExplanation, err := loadComparisonSide(ctx, name, "right", load, request.Right, state)
|
||||
acquired.Diagnostics = append(acquired.Diagnostics, rightDiagnostics...)
|
||||
acquired.Explanation = appendConfigKitExplanation(acquired.Explanation, rightExplanation)
|
||||
if err != nil {
|
||||
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: diagnostics}, err
|
||||
return acquired, 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)
|
||||
return acquired, fmt.Errorf("ConfigKit scope %q comparison before native diff: %w", name, err)
|
||||
}
|
||||
|
||||
differences, err := configkit.Diff(left, right)
|
||||
acquired.Value, 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 acquired, fmt.Errorf("ConfigKit scope %q native diff: %w", name, err)
|
||||
}
|
||||
return commandkit.Acquired[[]configkit.Difference]{Value: differences, Diagnostics: diagnostics}, nil
|
||||
return acquired, nil
|
||||
}
|
||||
return scope
|
||||
}
|
||||
@@ -90,67 +95,110 @@ func loadComparisonSide[I, S, T any](
|
||||
load func(context.Context, Selection, I, S) (*configkit.Result[T], error),
|
||||
request ComparisonSide[I],
|
||||
state S,
|
||||
) (*configkit.Result[T], []commandkit.Diagnostic, error) {
|
||||
) (*configkit.Result[T], []commandkit.Diagnostic, commandkit.Explanation, error) {
|
||||
result, err := load(ctx, request.Selection, request.Input, state)
|
||||
var diagnostics []commandkit.Diagnostic
|
||||
var explanation commandkit.Explanation
|
||||
if result != nil {
|
||||
warnings, terminal := configKitComparisonDiagnostics(scope, side, result.Sources(), result.Diagnostics())
|
||||
diagnostics = append(diagnostics, warnings...)
|
||||
explanation = appendConfigKitExplanation(explanation, terminal)
|
||||
}
|
||||
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())
|
||||
warnings, terminal := configKitComparisonDiagnostics(scope, side, loadError.Sources(), loadError.Diagnostics())
|
||||
diagnostics = append(diagnostics, warnings...)
|
||||
explanation = appendConfigKitExplanation(explanation, terminal)
|
||||
}
|
||||
return nil, diagnostics, fmt.Errorf("ConfigKit scope %q %s load: %w", scope, side, err)
|
||||
return result, diagnostics, explanation, 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 nil, diagnostics, explanation, fmt.Errorf("ConfigKit scope %q %s loader returned a nil result", scope, side)
|
||||
}
|
||||
return result, configKitComparisonDiagnostics(scope, side, result.Sources(), result.Diagnostics()), nil
|
||||
return result, diagnostics, explanation, nil
|
||||
}
|
||||
|
||||
func configKitDiagnostics(scope string, sources []configkit.SourceInfo, records []configkit.Diagnostic) []commandkit.Diagnostic {
|
||||
func configKitDiagnostics(scope string, sources []configkit.SourceInfo, records []configkit.Diagnostic) ([]commandkit.Diagnostic, commandkit.Explanation) {
|
||||
return configKitDiagnosticsWithHeading("Configuration scope "+strconv.Quote(scope)+":\n", sources, records)
|
||||
}
|
||||
|
||||
func configKitComparisonDiagnostics(scope, side string, sources []configkit.SourceInfo, records []configkit.Diagnostic) []commandkit.Diagnostic {
|
||||
func configKitComparisonDiagnostics(scope, side string, sources []configkit.SourceInfo, records []configkit.Diagnostic) ([]commandkit.Diagnostic, commandkit.Explanation) {
|
||||
return configKitDiagnosticsWithHeading("Configuration scope "+strconv.Quote(scope)+" "+side+":\n", sources, records)
|
||||
}
|
||||
|
||||
func configKitDiagnosticsWithHeading(heading string, sources []configkit.SourceInfo, records []configkit.Diagnostic) []commandkit.Diagnostic {
|
||||
func configKitDiagnosticsWithHeading(heading string, sources []configkit.SourceInfo, records []configkit.Diagnostic) ([]commandkit.Diagnostic, commandkit.Explanation) {
|
||||
if len(records) == 0 {
|
||||
return nil
|
||||
return nil, commandkit.Explanation{}
|
||||
}
|
||||
|
||||
severity := commandkit.SeverityWarning
|
||||
sources = append([]configkit.SourceInfo(nil), sources...)
|
||||
records = cloneConfigKitDiagnostics(records)
|
||||
warnings := make([]configkit.Diagnostic, 0, len(records))
|
||||
terminal := make([]configkit.Diagnostic, 0, len(records))
|
||||
for _, record := range records {
|
||||
if record.Severity == configkit.SeverityError {
|
||||
severity = commandkit.SeverityError
|
||||
break
|
||||
terminal = append(terminal, record)
|
||||
} else {
|
||||
warnings = append(warnings, record)
|
||||
}
|
||||
}
|
||||
|
||||
// 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, heading); err != nil {
|
||||
if len(warnings) == 0 {
|
||||
return nil, explanationWithConfigKitRecords(heading, sources, terminal)
|
||||
}
|
||||
warning := commandkit.Diagnostic{
|
||||
Severity: commandkit.SeverityWarning,
|
||||
Write: configKitDiagnosticWriter(heading, sources, warnings),
|
||||
}
|
||||
return []commandkit.Diagnostic{warning}, explanationWithConfigKitRecords(heading, sources, terminal)
|
||||
}
|
||||
|
||||
func explanationWithConfigKitRecords(heading string, sources []configkit.SourceInfo, records []configkit.Diagnostic) commandkit.Explanation {
|
||||
if len(records) == 0 {
|
||||
return commandkit.Explanation{}
|
||||
}
|
||||
return commandkit.Explanation{Details: []commandkit.Detail{configKitDiagnosticWriter(heading, sources, records)}}
|
||||
}
|
||||
|
||||
func configKitDiagnosticWriter(heading string, sources []configkit.SourceInfo, records []configkit.Diagnostic) commandkit.Detail {
|
||||
return func(writer io.Writer) error {
|
||||
if err := writeDiagnosticText(writer, heading); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(sources) > 0 {
|
||||
if err := writeDiagnosticText(writer, "Source IDs:\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(sources) > 0 {
|
||||
if err := writeDiagnosticText(writer, "Source IDs:\n"); err != nil {
|
||||
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
|
||||
}
|
||||
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)
|
||||
},
|
||||
}}
|
||||
}
|
||||
return configkit.WriteDiagnostics(writer, records)
|
||||
}
|
||||
}
|
||||
|
||||
func cloneConfigKitDiagnostics(records []configkit.Diagnostic) []configkit.Diagnostic {
|
||||
cloned := append([]configkit.Diagnostic(nil), records...)
|
||||
for index := range cloned {
|
||||
if records[index].Primary != nil {
|
||||
primary := *records[index].Primary
|
||||
cloned[index].Primary = &primary
|
||||
}
|
||||
cloned[index].Related = append([]configkit.Location(nil), records[index].Related...)
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
func appendConfigKitExplanation(left, right commandkit.Explanation) commandkit.Explanation {
|
||||
if left.Message == "" {
|
||||
left.Message = right.Message
|
||||
}
|
||||
left.Details = append(left.Details, right.Details...)
|
||||
return left
|
||||
}
|
||||
|
||||
func writeDiagnosticText(writer io.Writer, value string) error {
|
||||
|
||||
Reference in New Issue
Block a user