errbuilder

package module
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 17, 2025 License: MIT Imports: 9 Imported by: 0

README

ErrBuilder - A simple error builder for Go

A lightweight custom error builder library for Go.

I originally made this for my personal projects. It uses the Builder pattern to create a custom error object with a custom message and a custom error code. As well as convenience methods and an ErrMap type.

ErrMap is a string indexed map of error types.

The purpose of this library was to make a standard way of creating errors in my projects.

I wanted to create a robust and standardized meaning for certain error codes, I largely borrowed the gRPC error code specification as inspiration.

This is a general purpose library that can be used in any Go project. Please check out the examples directory for more information.

Installation

go get github.com/ZanzyTHEbar/errbuilder-go

Usage

package main

import (
    "context"
    "github.com/ZanzyTHEbar/errorbuilder"
)

func main() {
    // Create new ErrorMap to hold our error messages
    var errs errsx.ErrorMap

    if len(user.Username) < 4 {
	    errs.Set("username", "Username must be at least 4 characters")
    }
    if len(user.Password) < 8 {
	    errs.Set("password", "Password must be at least 8 characters")
    }
    if len(user.Email) == 0 {
	     errs.Set("email", "Email is required")
    }

    // Create custom error to handle our error messages
    customError := errbuilder.NewErrBuilder().
                    WithCode(errbuilder.CodeInvalidArgument).
		                WithMsg("Bad Request").
		                WithDetails(errbuilder.NewErrDetails(errs))

    // Check if there were any errors
    if errs != nil {
	    // Return the errors as a JSON response
	    return customError
    }
}

Features

  • Error Builder: Custom Error Builder for creating structured error messages to your requirements.
  • Error Codes: A ruch set of error codes, with an interface, for defining the type of error. Follows the gRPC error code specification.
  • Error Map: An optional and dynamic map to contain error messages for complex control flows, perhaps even deferred error handling.
  • Error Details: Custom ErrDetails type that allows providing extra data to be JSON (or other type) formatted into the error message.
  • Builtin Custom Error wrappers: (errors.go)[/errors.go] contains 5 builtin error functions that demonstrate how to use the errbuilder, and are provided for common error usage requirements.

Works very well with my (assert-lib)[https://github.com/ZanzyTHEbar/assert-lib] library.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenericErr added in v1.2.0

func GenericErr(msg string, err error) error

func InternalServerErr

func InternalServerErr(err error) error

func NotFoundErr

func NotFoundErr(err error) error

func UnauthorizedErr

func UnauthorizedErr(err error) error

func ValidationErr

func ValidationErr(errors ErrorMap) error

Example usage:

app.Post("/register", func(c *http.Ctx) error {
		// Define a struct for the request body
		type User struct {
			Username string `json:"username"`
			Password string `json:"password"`
			Email    string `json:"email"`
		}
		// simple validation for example purposes

		var user User
		// Parse the JSON body
		if err := c.BodyParser(&user); err != nil {
			return InvalidDataErr(err)
		}
		// Validate the data

		var errs errsx.ErrorMap
		if len(user.Username) < 4 {
			errs.Set("username", "Username must be at least 4 characters")
		}
		if len(user.Password) < 8 {
			errs.Set("password", "Password must be at least 8 characters")
		}
		if len(user.Email) == 0 {
			 errs.Set("email", "Email is required")
		}
		// Check if there were any errors
		if errs != nil {
			// Return the errors as a JSON response
			return c.Status(http.StatusUnprocessableEntity).JSON(ValidationErr(errors))
		}
		// Continue with user registration process...
		return c.SendStatus(http.StatusOK)
})

func WrapIfContextDone

func WrapIfContextDone(ctx context.Context, err error) error

wrapIfContextDone wraps errors with CodeCanceled or CodeDeadlineExceeded if the context is done. It leaves already-wrapped errors unchanged.

func WrapIfContextError

func WrapIfContextError(err error) error

wrapIfContextError applies CodeCanceled or CodeDeadlineExceeded to Go's context.Canceled and context.DeadlineExceeded errors, but only if they haven't already been wrapped.

func WrapIfLikelyH2CNotConfiguredError

func WrapIfLikelyH2CNotConfiguredError(request *http.Request, err error) error

wrapIfLikelyH2CNotConfiguredError adds a wrapping error that has a message telling the caller that they likely need to use h2c but are using a raw http.Client{}.

This happens when running a gRPC-only server. This is fragile and may break over time, and this should be considered a best-effort.

func WrapIfLikelyWithGRPCNotUsedError

func WrapIfLikelyWithGRPCNotUsedError(err error) error

wrapIfLikelyWithGRPCNotUsedError adds a wrapping error that has a message telling the caller that they likely forgot to use WithGRPC().

This happens when running a gRPC-only server. This is fragile and may break over time, and this should be considered a best-effort.

func WrapIfMaxBytesError

func WrapIfMaxBytesError(err error, tmpl string, args ...any) error

wrapIfMaxBytesError wraps errors returned reading from a http.MaxBytesHandler whose limit has been exceeded.

func WrapIfRSTError

func WrapIfRSTError(err error) error

HTTP/2 has its own set of error codes, which it sends in RST_STREAM frames. When the server sends one of these errors, we should map it back into our RPC error codes following https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#http2-transport-mapping.

This would be vastly simpler if we were using x/net/http2 directly, since the StreamError type is exported. When x/net/http2 gets vendored into net/http, though, all these types become unexported...so we're left with string munging.

func WrapIfUncoded

func WrapIfUncoded(err error) error

wrapIfUncoded ensures that all errors are wrapped. It leaves already-wrapped errors unchanged, uses wrapIfContextError to apply codes to context.Canceled and context.DeadlineExceeded, and falls back to wrapping other errors with CodeUnknown.

Types

type ErrBuilder

type ErrBuilder struct {
	Code    ErrCode    `json:"code"`
	Msg     string     `json:"message"`
	Cause   error      `json:"Cause"`
	Label   string     `json:"label"`
	Details ErrDetails `json:"details"`
}

func NewErrBuilder

func NewErrBuilder() *ErrBuilder

NewErrBuilder is a constructor for ErrBuilder

func (*ErrBuilder) ErrCode

func (err *ErrBuilder) ErrCode() ErrCode

Code returns the error's status code.

func (*ErrBuilder) Error

func (builder *ErrBuilder) Error() string

Error is a method to return an error, this is an implementation of the error interface.

func (*ErrBuilder) MarshalJSON

func (builder *ErrBuilder) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (*ErrBuilder) Unwrap

func (err *ErrBuilder) Unwrap() error

Unwrap allows errors.Is and errors.As access to the underlying error.

func (*ErrBuilder) WithCause

func (builder *ErrBuilder) WithCause(Cause error) *ErrBuilder

WithCause is a method to set the error Cause.

func (*ErrBuilder) WithCode

func (builder *ErrBuilder) WithCode(code ErrCode) *ErrBuilder

WithCode is a method to set the error code.

func (*ErrBuilder) WithDetails

func (builder *ErrBuilder) WithDetails(details ErrDetails) *ErrBuilder

WithDetails is a method to set the error details.

func (*ErrBuilder) WithLabel

func (builder *ErrBuilder) WithLabel(label string) *ErrBuilder

WithLabel is a method to set the error label.

func (*ErrBuilder) WithMsg

func (builder *ErrBuilder) WithMsg(msg string) *ErrBuilder

WithMsg is a method to set the error message.

type ErrCode

type ErrCode uint32
const (

	// CodeCanceled indicates that the operation was canceled, typically by the
	// caller.
	CodeCanceled ErrCode = 1

	// CodeUnknown indicates that the operation failed for an unknown reason.
	CodeUnknown ErrCode = 2

	// CodeInvalidArgument indicates that client supplied an invalid argument.
	CodeInvalidArgument ErrCode = 3

	// CodeDeadlineExceeded indicates that deadline expired before the operation
	// could complete.
	CodeDeadlineExceeded ErrCode = 4

	// CodeNotFound indicates that some requested entity (for example, a file or
	// directory) was not found.
	CodeNotFound ErrCode = 5

	// CodeAlreadyExists indicates that client attempted to create an entity (for
	// example, a file or directory) that already exists.
	CodeAlreadyExists ErrCode = 6

	// CodePermissionDenied indicates that the caller doesn't have permission to
	// execute the specified operation.
	CodePermissionDenied ErrCode = 7

	// CodeResourceExhausted indicates that some resource has been exhausted. For
	// example, a per-user quota may be exhausted or the entire file system may
	// be full.
	CodeResourceExhausted ErrCode = 8

	// CodeFailedPrecondition indicates that the system is not in a state
	// required for the operation's execution.
	CodeFailedPrecondition ErrCode = 9

	// CodeAborted indicates that operation was aborted by the system, usually
	// because of a concurrency issue such as a sequencer check failure or
	// transaction abort.
	CodeAborted ErrCode = 10

	// CodeOutOfRange indicates that the operation was attempted past the valid
	// range (for example, seeking past end-of-file).
	CodeOutOfRange ErrCode = 11

	// CodeUnimplemented indicates that the operation isn't implemented,
	// supported, or enabled in this service.
	CodeUnimplemented ErrCode = 12

	// CodeInternal indicates that some invariants expected by the underlying
	// system have been broken. This code is reserved for serious errors.
	CodeInternal ErrCode = 13

	// CodeUnavailable indicates that the service is currently unavailable. This
	// is usually temporary, so clients can back off and retry idempotent
	// operations.
	CodeUnavailable ErrCode = 14

	// CodeDataLoss indicates that the operation has resulted in unrecoverable
	// data loss or corruption.
	CodeDataLoss ErrCode = 15

	// CodeUnauthenticated indicates that the request does not have valid
	// authentication credentials for the operation.
	CodeUnauthenticated ErrCode = 16
)

func CodeOf

func CodeOf(err error) ErrCode

CodeOf returns the error's status code if it is or wraps an *ErrBuilder and CodeUnknown otherwise.

func (ErrCode) MarshalText

func (c ErrCode) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (ErrCode) String

func (c ErrCode) String() string

func (*ErrCode) UnmarshalText

func (c *ErrCode) UnmarshalText(data []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

type ErrDetails

type ErrDetails struct {
	Errors ErrorMap `json:"errors"`
}

func NewErrDetails

func NewErrDetails(errors ErrorMap) ErrDetails

NewErrDetails is a constructor for ErrDetails

func (*ErrDetails) UnWrap

func (err *ErrDetails) UnWrap() (ErrorMap, error)

UnWrap is a method to return the error details as a map of errors.

type ErrorMap

type ErrorMap map[string]error

ErrorMap represents a collection of errors keyed by name.

func (ErrorMap) Error

func (m ErrorMap) Error() string

func (ErrorMap) Get

func (m ErrorMap) Get(key string) string

Get will return the error string for the given key.

func (*ErrorMap) Has

func (m *ErrorMap) Has(key string) bool

func (ErrorMap) MarshalJSON

func (m ErrorMap) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (*ErrorMap) Set

func (m *ErrorMap) Set(key string, msg any)

Set associates the given error with the given key. The map is lazily instantiated if it is nil.

func (ErrorMap) String

func (m ErrorMap) String() string

func (ErrorMap) ToError

func (m ErrorMap) ToError(msg string) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL