mirror of
https://github.com/aykhans/dodo.git
synced 2025-09-01 00:53:34 +00:00
Add -f/--config-file flag for loading YAML configs from local or remote sources. Fix error handling to return unmatched errors.
106 lines
2.9 KiB
Go
106 lines
2.9 KiB
Go
package utils
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
// ErrorHandler represents a function that handles a specific error type
|
|
type ErrorHandler func(error) error
|
|
|
|
// ErrorMatcher holds the error type/value and its handler
|
|
type ErrorMatcher struct {
|
|
ErrorType any // Can be error value (sentinel) or error type
|
|
Handler ErrorHandler
|
|
IsSentinel bool // true for sentinel errors, false for custom types
|
|
}
|
|
|
|
// HandleError processes an error against a list of matchers and executes the appropriate handler.
|
|
// It returns (true, handlerResult) if a matching handler is found and executed,
|
|
// or (false, nil) if no matcher matches the error.
|
|
// If err is nil, returns (true, nil).
|
|
//
|
|
// Example:
|
|
//
|
|
// handled, result := HandleError(err,
|
|
// OnSentinelError(io.EOF, func(e error) error {
|
|
// return nil // EOF is expected, ignore it
|
|
// }),
|
|
// OnCustomError(func(e *CustomError) error {
|
|
// return fmt.Errorf("custom error: %w", e)
|
|
// }),
|
|
// )
|
|
func HandleError(err error, matchers ...ErrorMatcher) (bool, error) {
|
|
if err == nil {
|
|
return true, nil
|
|
}
|
|
|
|
for _, matcher := range matchers {
|
|
if matcher.IsSentinel {
|
|
// Handle sentinel errors with errors.Is
|
|
if sentinelErr, ok := matcher.ErrorType.(error); ok {
|
|
if errors.Is(err, sentinelErr) {
|
|
return true, matcher.Handler(err)
|
|
}
|
|
}
|
|
} else {
|
|
// Handle custom error types with errors.As
|
|
errorType := reflect.TypeOf(matcher.ErrorType)
|
|
errorValue := reflect.New(errorType).Interface()
|
|
|
|
if errors.As(err, errorValue) {
|
|
return true, matcher.Handler(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return false, err // No matcher found
|
|
}
|
|
|
|
// HandleErrorOrDie processes an error against a list of matchers and executes the appropriate handler.
|
|
// If a matching handler is found, it returns the handler's result.
|
|
// If no matcher matches the error, it panics with a descriptive message.
|
|
// This function is useful when all expected error types must be handled explicitly.
|
|
//
|
|
// Example:
|
|
//
|
|
// result := HandleErrorOrDie(err,
|
|
// OnSentinelError(context.Canceled, func(e error) error {
|
|
// return fmt.Errorf("operation canceled")
|
|
// }),
|
|
// OnCustomError(func(e *ValidationError) error {
|
|
// return fmt.Errorf("validation failed: %w", e)
|
|
// }),
|
|
// ) // Panics if err doesn't match any handler
|
|
func HandleErrorOrDie(err error, matchers ...ErrorMatcher) error {
|
|
ok, err := HandleError(err, matchers...)
|
|
if !ok {
|
|
panic(fmt.Sprintf("Unhandled error of type %T: %v", err, err))
|
|
}
|
|
return err
|
|
}
|
|
|
|
func OnSentinelError(sentinelErr error, handler ErrorHandler) ErrorMatcher {
|
|
return ErrorMatcher{
|
|
ErrorType: sentinelErr,
|
|
Handler: handler,
|
|
IsSentinel: true,
|
|
}
|
|
}
|
|
|
|
func OnCustomError[T error](handler func(T) error) ErrorMatcher {
|
|
var zero T
|
|
return ErrorMatcher{
|
|
ErrorType: zero,
|
|
Handler: func(err error) error {
|
|
var typedErr T
|
|
if errors.As(err, &typedErr) {
|
|
return handler(typedErr)
|
|
}
|
|
return nil
|
|
},
|
|
IsSentinel: false,
|
|
}
|
|
}
|