Preserve structured diagnostics and candidate error context
This commit is contained in:
@@ -2,8 +2,7 @@ package releasekit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"slices"
|
||||
|
||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/validation"
|
||||
)
|
||||
@@ -32,21 +31,24 @@ type FileSizeMeasurement struct {
|
||||
// ValidationError describes a failed shared validation check returned by
|
||||
// [Check] or [Publish]. Cause preserves the contextual and underlying error chain.
|
||||
type ValidationError struct {
|
||||
Check ValidationCheck // failed shared check
|
||||
Findings []ValidationFinding // actionable observations, when available
|
||||
Check ValidationCheck // failed shared check
|
||||
Findings []ValidationFinding // actionable observations, when available
|
||||
Measurement *FileSizeMeasurement // measured file size, when applicable
|
||||
Cause error // original contextual and underlying error chain
|
||||
Cause error // original contextual and underlying error chain
|
||||
}
|
||||
|
||||
// Error returns the contextual validation error summary.
|
||||
// Error returns check details alongside the contextual and underlying causes.
|
||||
func (failure *ValidationError) Error() string {
|
||||
if failure == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
if failure.Cause != nil {
|
||||
diagnostic := internalValidationFailure(failure)
|
||||
var original *validation.Failure
|
||||
if errors.As(failure.Cause, &original) && sameValidationDiagnostics(diagnostic, original) {
|
||||
// Converted causes already render these diagnostics and candidate context.
|
||||
return failure.Cause.Error()
|
||||
}
|
||||
return formatPublicValidationError(failure)
|
||||
return diagnostic.Error()
|
||||
}
|
||||
|
||||
// Unwrap returns the original validation error chain.
|
||||
@@ -61,6 +63,10 @@ func convertValidationError(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
var publicFailure *ValidationError
|
||||
if errors.As(err, &publicFailure) {
|
||||
return err
|
||||
}
|
||||
var failure *validation.Failure
|
||||
if !errors.As(err, &failure) {
|
||||
return err
|
||||
@@ -91,31 +97,30 @@ func convertValidationError(err error) error {
|
||||
return result
|
||||
}
|
||||
|
||||
func formatPublicValidationError(failure *ValidationError) string {
|
||||
var summary strings.Builder
|
||||
if failure.Check == "" {
|
||||
summary.WriteString("validation failed")
|
||||
} else {
|
||||
fmt.Fprintf(&summary, "%s validation failed", failure.Check)
|
||||
}
|
||||
func internalValidationFailure(failure *ValidationError) *validation.Failure {
|
||||
result := &validation.Failure{Check: validation.Check(failure.Check), Cause: failure.Cause}
|
||||
for _, finding := range failure.Findings {
|
||||
summary.WriteString(": ")
|
||||
if finding.Path != "" {
|
||||
summary.WriteString(finding.Path)
|
||||
if finding.Line > 0 {
|
||||
fmt.Fprintf(&summary, ":%d", finding.Line)
|
||||
if finding.Column > 0 {
|
||||
fmt.Fprintf(&summary, ":%d", finding.Column)
|
||||
}
|
||||
}
|
||||
summary.WriteString(": ")
|
||||
}
|
||||
summary.WriteString(finding.Message)
|
||||
result.Findings = append(result.Findings, validation.Finding{
|
||||
Path: finding.Path, Line: finding.Line, Column: finding.Column,
|
||||
Message: finding.Message, Target: finding.Target, Directive: finding.Directive,
|
||||
})
|
||||
}
|
||||
if failure.Measurement != nil {
|
||||
fmt.Fprintf(&summary, ": measured %d bytes against a %d byte limit", failure.Measurement.Bytes, failure.Measurement.LimitBytes)
|
||||
result.Measurement = &validation.FileSizeMeasurement{
|
||||
Bytes: failure.Measurement.Bytes, LimitBytes: failure.Measurement.LimitBytes,
|
||||
}
|
||||
}
|
||||
return summary.String()
|
||||
return result
|
||||
}
|
||||
|
||||
func sameValidationDiagnostics(a, b *validation.Failure) bool {
|
||||
if b == nil || a.Check != b.Check || !slices.Equal(a.Findings, b.Findings) {
|
||||
return false
|
||||
}
|
||||
if a.Measurement == nil || b.Measurement == nil {
|
||||
return a.Measurement == b.Measurement
|
||||
}
|
||||
return *a.Measurement == *b.Measurement
|
||||
}
|
||||
|
||||
var _ error = (*ValidationError)(nil)
|
||||
|
||||
Reference in New Issue
Block a user