Skip to content

Commit

Permalink
Remove unnecessary generics
Browse files Browse the repository at this point in the history
  • Loading branch information
Maarten van der Heijden committed Aug 8, 2023
1 parent 7aca5db commit 26737ad
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 248 deletions.
91 changes: 14 additions & 77 deletions v2/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,6 @@ var DefaultErrorRegistry = NewErrorRegistry()
type internalHandler func(ctx context.Context, err error) (int, any)
type internalStringHandler func(ctx context.Context, err string) (int, any)

// CustomErrorHandler is the template for unexported errors. For example binding.SliceValidationError
// or uuid.invalidLengthError
type CustomErrorHandler interface {
func(err error) (int, any) |
func(ctx context.Context, err error) (int, any)
}

// ErrorStringHandler is the template for string errors that don't have their own object available. For example
// "record not found" or "invalid input"
type ErrorStringHandler interface {
func(err string) (int, any) |
func(ctx context.Context, err string) (int, any)
}

// ErrorHandler is the template of an error handler in the ErrorRegistry. The E type is the error type that
// the handler is registered for. The R type is the type of the response body.
type ErrorHandler[E error] interface {
Expand Down Expand Up @@ -78,25 +64,13 @@ func (e *ErrorRegistry) SetDefaultResponse(code int, response any) {

// NewErrorResponse Returns an error response using the DefaultErrorRegistry. If no specific handler could be found,
// it will return the defaults.
func NewErrorResponse(err error) (int, any) {
return NewErrorResponseFrom(DefaultErrorRegistry, err)
}

// NewContextErrorResponse Returns an error response using the DefaultErrorRegistry. If no specific handler could be found,
// it will return the defaults.
func NewContextErrorResponse(ctx context.Context, err error) (int, any) {
return NewContextErrorResponseFrom(DefaultErrorRegistry, ctx, err)
func NewErrorResponse(ctx context.Context, err error) (int, any) {
return NewErrorResponseFrom(DefaultErrorRegistry, ctx, err)
}

// NewErrorResponseFrom Returns an error response using the given registry. If no specific handler could be found,
// it will return the defaults.
func NewErrorResponseFrom(registry *ErrorRegistry, err error) (int, any) {
return NewContextErrorResponseFrom(registry, context.Background(), err)
}

// NewContextErrorResponseFrom Returns an error response using the given registry. If no specific handler could be found,
// it will return the defaults.
func NewContextErrorResponseFrom(registry *ErrorRegistry, ctx context.Context, err error) (int, any) {
func NewErrorResponseFrom(registry *ErrorRegistry, ctx context.Context, err error) (int, any) {
errorType := fmt.Sprintf("%T", err)

// If a handler is registered for the error type, use it.
Expand All @@ -110,84 +84,47 @@ func NewContextErrorResponseFrom(registry *ErrorRegistry, ctx context.Context, e
}

// RegisterErrorHandler registers an error handler in DefaultErrorRegistry. The R type is the type of the response body.
func RegisterErrorHandler[E error, F ErrorHandler[E]](err E, handler F) {
RegisterErrorHandlerOn[E](DefaultErrorRegistry, err, handler)
func RegisterErrorHandler[E error](handler func(context.Context, E) (int, any)) {
RegisterErrorHandlerOn(DefaultErrorRegistry, handler)
}

// RegisterErrorHandlerOn registers an error handler in the given registry. The R type is the type of the response body.
func RegisterErrorHandlerOn[E error, F ErrorHandler[E]](registry *ErrorRegistry, err E, handler F) {
func RegisterErrorHandlerOn[E error](registry *ErrorRegistry, handler func(context.Context, E) (int, any)) {
// Name of the type
errorType := fmt.Sprintf("%T", err)
errorType := fmt.Sprintf("%T", *new(E))

// Wrap it in a closure, we can't save it directly because err E is not available in NewErrorResponseFrom. It will
// be available in the closure when it is called. Check out TestErrorResponseFrom_ReturnsErrorBInInterface for an example.
registry.handlers[errorType] = func(ctx context.Context, err error) (int, any) {
switch handler := any(handler).(type) {
case func(context.Context, E) (int, any):
// We can safely cast it here, because we know it's the right type.
return handler(ctx, err.(E))

case func(E) (int, any):
// We can safely cast it here, because we know it's the right type.
return handler(err.(E))

default:
panic("impossible path")
}
return handler(ctx, err.(E))
}
}

// RegisterCustomErrorTypeHandler registers an error handler in DefaultErrorRegistry. Same as RegisterErrorHandler,
// but you can set the fmt.Sprint("%T", err) error yourself. Allows you to register error types that aren't exported
// from their respective packages such as the uuid error or *errors.errorString. The R type is the type of the response body.
func RegisterCustomErrorTypeHandler[F CustomErrorHandler](errorType string, handler F) {
func RegisterCustomErrorTypeHandler(errorType string, handler func(ctx context.Context, err error) (int, any)) {
RegisterCustomErrorTypeHandlerOn(DefaultErrorRegistry, errorType, handler)
}

// RegisterCustomErrorTypeHandlerOn registers an error handler in the given registry. Same as RegisterErrorHandlerOn,
// but you can set the fmt.Sprint("%T", err) error yourself. Allows you to register error types that aren't exported
// from their respective packages such as the uuid error or *errors.errorString. The R type is the type of the response body.
func RegisterCustomErrorTypeHandlerOn[F CustomErrorHandler](registry *ErrorRegistry, errorType string, handler F) {
func RegisterCustomErrorTypeHandlerOn(registry *ErrorRegistry, errorType string, handler func(ctx context.Context, err error) (int, any)) {
// Wrap it in a closure, we can't save it directly
registry.handlers[errorType] = func(ctx context.Context, err error) (int, any) {
switch handler := any(handler).(type) {
case func(context.Context, error) (int, any):
// We can safely cast it here, because we know it's the right type.
return handler(ctx, err)

case func(error) (int, any):
// We can safely cast it here, because we know it's the right type.
return handler(err)

default:
panic("impossible path")
}
}
registry.handlers[errorType] = handler
}

// RegisterStringErrorHandler allows you to register an error handler for a simple errorString created with
// errors.New() or fmt.Errorf(). Can be used in case you are dealing with libraries that don't have exported
// error objects. Uses the DefaultErrorRegistry. The R type is the type of the response body.
func RegisterStringErrorHandler[F ErrorStringHandler](errorString string, handler F) {
func RegisterStringErrorHandler(errorString string, handler func(ctx context.Context, err string) (int, any)) {
RegisterStringErrorHandlerOn(DefaultErrorRegistry, errorString, handler)
}

// RegisterStringErrorHandlerOn allows you to register an error handler for a simple errorString created with
// errors.New() or fmt.Errorf(). Can be used in case you are dealing with libraries that don't have exported
// error objects. The R type is the type of the response body.
func RegisterStringErrorHandlerOn[F ErrorStringHandler](registry *ErrorRegistry, errorString string, handler F) {
registry.stringHandlers[errorString] = func(ctx context.Context, err string) (int, any) {
switch handler := any(handler).(type) {
case func(context.Context, string) (int, any):
// We can safely cast it here, because we know it's the right type.
return handler(ctx, err)

case func(string) (int, any):
// We can safely cast it here, because we know it's the right type.
return handler(err)

default:
panic("impossible path")
}
}
func RegisterStringErrorHandlerOn(registry *ErrorRegistry, errorString string, handler func(ctx context.Context, err string) (int, any)) {
registry.stringHandlers[errorString] = handler
}
Loading

0 comments on commit 26737ad

Please sign in to comment.