mirror of
https://github.com/aykhans/go-utils.git
synced 2025-10-15 18:25:57 +00:00
first commit
This commit is contained in:
146
errors/handler.go
Normal file
146
errors/handler.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package errors
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// OnSentinelError creates an ErrorMatcher for sentinel errors.
|
||||
// Sentinel errors are predefined error values that are compared using errors.Is.
|
||||
//
|
||||
// This is used with HandleError or HandleErrorOrDie to match specific error
|
||||
// values like io.EOF, context.Canceled, or custom sentinel errors defined with
|
||||
// errors.New or fmt.Errorf.
|
||||
//
|
||||
// The handler function receives the original error and can return a new error
|
||||
// or nil to suppress it.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// matcher := OnSentinelError(io.EOF, func(e error) error {
|
||||
// log.Println("reached end of file")
|
||||
// return nil // suppress EOF error
|
||||
// })
|
||||
func OnSentinelError(sentinelErr error, handler ErrorHandler) ErrorMatcher {
|
||||
return ErrorMatcher{
|
||||
ErrorType: sentinelErr,
|
||||
Handler: handler,
|
||||
IsSentinel: true,
|
||||
}
|
||||
}
|
||||
|
||||
// OnCustomError creates an ErrorMatcher for custom error types.
|
||||
// Custom error types are struct types that implement the error interface,
|
||||
// and are matched using errors.As to unwrap error chains.
|
||||
//
|
||||
// The type parameter T specifies the error type to match. The handler function
|
||||
// receives the unwrapped typed error, allowing you to access type-specific fields
|
||||
// and methods.
|
||||
//
|
||||
// This is particularly useful for handling errors with additional context or data,
|
||||
// such as validation errors, network errors, or domain-specific errors.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type ValidationError struct {
|
||||
// Field string
|
||||
// Msg string
|
||||
// }
|
||||
// func (e *ValidationError) Error() string {
|
||||
// return fmt.Sprintf("%s: %s", e.Field, e.Msg)
|
||||
// }
|
||||
//
|
||||
// matcher := OnCustomError(func(e *ValidationError) error {
|
||||
// log.Printf("validation failed on field %s: %s", e.Field, e.Msg)
|
||||
// return fmt.Errorf("invalid input: %w", e)
|
||||
// })
|
||||
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,
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user