package releasekit import ( "errors" "fmt" "strings" "gitea.maximumdirect.net/eric/ReleaseKit/internal/validation" ) // ValidationCheck identifies a shared validation check that failed. // Current checks include "go-test". type ValidationCheck string // ValidationFinding describes a failed check observation at an optional // repository-relative source location. type ValidationFinding struct { Path string // repository-relative path when available Line int // one-based source line, or zero when unavailable Column int // one-based source column, or zero when unavailable Message string // useful detail that is not promised to have exact wording Target string // applicable target for a file check Directive string // applicable directive for a file check } // FileSizeMeasurement reports a measured file size and its selected limit. type FileSizeMeasurement struct { Bytes int64 LimitBytes int64 } // 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 Measurement *FileSizeMeasurement // measured file size, when applicable Cause error // original contextual and underlying error chain } // Error returns the contextual validation error summary. func (failure *ValidationError) Error() string { if failure == nil { return "" } if failure.Cause != nil { return failure.Cause.Error() } return formatPublicValidationError(failure) } // Unwrap returns the original validation error chain. func (failure *ValidationError) Unwrap() error { if failure == nil { return nil } return failure.Cause } func convertValidationError(err error) error { if err == nil { return nil } var failure *validation.Failure if !errors.As(err, &failure) { return err } result := &ValidationError{ Check: ValidationCheck(failure.Check), Cause: err, } if len(failure.Findings) != 0 { result.Findings = make([]ValidationFinding, len(failure.Findings)) for index, finding := range failure.Findings { result.Findings[index] = ValidationFinding{ Path: finding.Path, Line: finding.Line, Column: finding.Column, Message: finding.Message, Target: finding.Target, Directive: finding.Directive, } } } if failure.Measurement != nil { result.Measurement = &FileSizeMeasurement{ Bytes: failure.Measurement.Bytes, LimitBytes: failure.Measurement.LimitBytes, } } 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) } 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) } if failure.Measurement != nil { fmt.Fprintf(&summary, ": measured %d bytes against a %d byte limit", failure.Measurement.Bytes, failure.Measurement.LimitBytes) } return summary.String() } var _ error = (*ValidationError)(nil)