errors

package module
v0.8.1 Latest Latest
Warning

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

Go to latest
Published: Feb 23, 2022 License: MIT Imports: 6 Imported by: 1,863

README

Emperror: Errors Mentioned in Awesome Go

GitHub Workflow Status Codecov Go Report Card Go Version PkgGoDev FOSSA Status

Drop-in replacement for the standard library errors package and github.com/pkg/errors.

This is a single, lightweight library merging the features of standard library errors package and github.com/pkg/errors. It also backports a few features (like Go 1.13 error handling related features).

Standard library features:

  • New creates an error with stack trace
  • Unwrap supports both Go 1.13 wrapper (interface { Unwrap() error }) and pkg/errors causer (interface { Cause() error }) interface
  • Backported Is and As functions

github.com/pkg/errors features:

  • New, Errorf, WithMessage, WithMessagef, WithStack, Wrap, Wrapf functions behave the same way as in the original library
  • Cause supports both Go 1.13 wrapper (interface { Unwrap() error }) and pkg/errors causer (interface { Cause() error }) interface

Additional features:

  • NewPlain creates a new error without any attached context, like stack trace
  • Sentinel is a shorthand type for creating constant error
  • WithStackDepth allows attaching stack trace with a custom caller depth
  • WithStackDepthIf, WithStackIf, WrapIf, WrapIff only annotate errors with a stack trace if there isn't one already in the error chain
  • Multi error aggregating multiple errors into a single value
  • NewWithDetails, WithDetails and Wrap*WithDetails functions to add key-value pairs to an error
  • Match errors using the match package

Installation

go get emperror.dev/errors

Usage

package main

import "emperror.dev/errors"

// ErrSomethingWentWrong is a sentinel error which can be useful within a single API layer.
const ErrSomethingWentWrong = errors.Sentinel("something went wrong")

// ErrMyError is an error that can be returned from a public API.
type ErrMyError struct {
	Msg string
}

func (e ErrMyError) Error() string {
	return e.Msg
}

func foo() error {
	// Attach stack trace to the sentinel error.
	return errors.WithStack(ErrSomethingWentWrong)
}

func bar() error {
	return errors.Wrap(ErrMyError{"something went wrong"}, "error")
}

func main() {
	if err := foo(); err != nil {
		if errors.Cause(err) == ErrSomethingWentWrong { // or errors.Is(ErrSomethingWentWrong)
			// handle error
		}
	}

	if err := bar(); err != nil {
		if errors.As(err, &ErrMyError{}) {
			// handle error
		}
	}
}

Match errors:

package main

import (
    "emperror.dev/errors"
    "emperror.dev/errors/match"
)

// ErrSomethingWentWrong is a sentinel error which can be useful within a single API layer.
const ErrSomethingWentWrong = errors.Sentinel("something went wrong")

type clientError interface{
    ClientError() bool
}

func foo() error {
	// Attach stack trace to the sentinel error.
	return errors.WithStack(ErrSomethingWentWrong)
}

func main() {
    var ce clientError
    matcher := match.Any{match.As(&ce), match.Is(ErrSomethingWentWrong)}

	if err := foo(); err != nil {
		if matcher.MatchError(err) {
			// you can use matchers to write complex conditions for handling (or not) an error
            // used in emperror
		}
	}
}

Development

Contributions are welcome! :)

  1. Clone the repository
  2. Make changes on a new branch
  3. Run the test suite:
    ./pleasew build
    ./pleasew test
    ./pleasew gotest
    ./pleasew lint
    
  4. Commit, push and open a PR

License

The MIT License (MIT). Please see License File for more information.

Certain parts of this library are inspired by (or entirely copied from) various third party libraries. Their licenses can be found in the Third Party License File.

FOSSA Status

Documentation

Overview

Package errors is a drop-in replacement for the standard errors package and github.com/pkg/errors.

Overview

This is a single, lightweight library merging the features of standard library `errors` package and https://github.com/pkg/errors. It also backports a few features (like Go 1.13 error handling related features).

Printing errors

If not stated otherwise, errors can be formatted with the following specifiers:

%s	error message
%q	double-quoted error message
%v	error message in default format
%+v	error message and stack trace

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Append added in v0.3.0

func Append(left error, right error) error

Append appends the given errors together. Either value may be nil.

This function is a specialization of Combine for the common case where there are only two errors.

err = errors.Append(reader.Close(), writer.Close())

The following pattern may also be used to record failure of deferred operations without losing information about the original error.

func doSomething(..) (err error) {
	f := acquireResource()
	defer func() {
		err = errors.Append(err, f.Close())
	}()
Example
package main

import (
	"fmt"

	"emperror.dev/errors"
)

func main() {
	var err error
	err = errors.Append(err, errors.NewPlain("call 1 failed"))
	err = errors.Append(err, errors.NewPlain("call 2 failed"))
	fmt.Println(err)
}
Output:

call 1 failed; call 2 failed

func As

func As(err error, target interface{}) bool

As finds the first error in err's chain that matches the type to which target points, and if so, sets the target to its value and returns true. An error matches a type if it is assignable to the target type, or if it has a method As(interface{}) bool such that As(target) returns true. As will panic if target is not a non-nil pointer to a type which implements error or is of interface type.

The As method should set the target to its value and return true if err matches the type to which target points.

Example
package main

import (
	"fmt"
	"os"

	"emperror.dev/errors"
)

func main() {
	if _, err := os.Open("non-existing"); err != nil {
		var pathError *os.PathError
		if errors.As(err, &pathError) {
			fmt.Println("Failed at path:", pathError.Path)
		} else {
			fmt.Println(err)
		}
	}

}
Output:

Failed at path: non-existing

func Cause

func Cause(err error) error

Cause returns the last error (root cause) in an err's chain. If err has no chain, it is returned directly.

It supports both Go 1.13 errors.Wrapper and github.com/pkg/errors.Causer interfaces (the former takes precedence).

Example
package main

import (
	"fmt"

	"emperror.dev/errors"
)

func main() {
	ErrSomething := errors.NewPlain("something")

	if err := errors.Wrap(ErrSomething, "error"); err != nil {
		if errors.Cause(err) == ErrSomething {
			fmt.Println("something went wrong")
		} else {
			fmt.Println(err)
		}
	}

}
Output:

something went wrong

func Combine added in v0.3.0

func Combine(errors ...error) error

Combine combines the passed errors into a single error.

If zero arguments were passed or if all items are nil, a nil error is returned.

If only a single error was passed, it is returned as-is.

Combine omits nil errors so this function may be used to combine together errors from operations that fail independently of each other.

errors.Combine(
	reader.Close(),
	writer.Close(),
	pipe.Close(),
)

If any of the passed errors is already an aggregated error, it will be flattened along with the other errors.

errors.Combine(errors.Combine(err1, err2), err3)
// is the same as
errors.Combine(err1, err2, err3)

The returned error formats into a readable multi-line error message if formatted with %+v.

fmt.Sprintf("%+v", errors.Combine(err1, err2))
Example
package main

import (
	"fmt"

	"emperror.dev/errors"
)

func main() {
	err := errors.Combine(
		errors.NewPlain("call 1 failed"),
		nil, // successful request
		errors.NewPlain("call 3 failed"),
		nil, // successful request
		errors.NewPlain("call 5 failed"),
	)
	fmt.Printf("%+v", err)
}
Output:

the following errors occurred:
 -  call 1 failed
 -  call 3 failed
 -  call 5 failed
Example (Loop)
package main

import (
	"fmt"

	"emperror.dev/errors"
)

func main() {
	var errs []error

	for i := 1; i < 6; i++ {
		if i%2 == 0 {
			continue
		}

		err := errors.NewPlain(fmt.Sprintf("call %d failed", i))
		errs = append(errs, err)
	}

	err := errors.Combine(errs...)

	fmt.Printf("%+v", err)
}
Output:

the following errors occurred:
 -  call 1 failed
 -  call 3 failed
 -  call 5 failed

func Errorf

func Errorf(format string, a ...interface{}) error

Errorf returns a new error with a formatted message and annotated with stack trace at the point Errorf is called.

err := errors.Errorf("something went %s", "wrong")

func GetDetails added in v0.4.0

func GetDetails(err error) []interface{}

GetDetails extracts the key-value pairs from err's chain.

func GetErrors added in v0.3.0

func GetErrors(err error) []error

GetErrors returns a slice containing zero or more errors that the supplied error is composed of. If the error is nil, the returned slice is empty.

err := errors.Append(r.Close(), w.Close())
errors := errors.GetErrors(err)

If the error is not composed of other errors, the returned slice contains just the error that was passed in.

Callers of this function are free to modify the returned slice.

Example
package main

import (
	"fmt"

	"emperror.dev/errors"
)

func main() {
	err := errors.Combine(
		nil, // successful request
		errors.NewPlain("call 2 failed"),
		errors.NewPlain("call 3 failed"),
	)
	err = errors.Append(err, nil) // successful request
	err = errors.Append(err, errors.NewPlain("call 5 failed"))

	errs := errors.GetErrors(err)
	for _, err := range errs {
		fmt.Println(err)
	}
}
Output:

call 2 failed
call 3 failed
call 5 failed

func Is

func Is(err, target error) bool

Is reports whether any error in err's chain matches target.

An error is considered to match a target if it is equal to that target or if it implements a method Is(error) bool such that Is(target) returns true.

Example
package main

import (
	"fmt"

	"emperror.dev/errors"
)

func main() {
	ErrSomething := errors.NewPlain("something")

	if err := errors.Wrap(ErrSomething, "error"); err != nil {
		if errors.Is(err, ErrSomething) {
			fmt.Println("something went wrong")
		} else {
			fmt.Println(err)
		}
	}

}
Output:

something went wrong

func New

func New(message string) error

New returns a new error annotated with stack trace at the point New is called.

New is a shorthand for:

WithStack(NewPlain(message))

func NewPlain

func NewPlain(message string) error

NewPlain returns a simple error without any annotated context, like stack trace. Useful for creating sentinel errors and in testing.

var ErrSomething = errors.NewPlain("something went wrong")

func NewWithDetails added in v0.4.2

func NewWithDetails(message string, details ...interface{}) error

NewWithDetails returns a new error annotated with stack trace at the point NewWithDetails is called, and the supplied details.

func Unwrap

func Unwrap(err error) error

Unwrap returns the result of calling the Unwrap method on err, if err implements Unwrap. Otherwise, Unwrap returns nil.

It supports both Go 1.13 Unwrap and github.com/pkg/errors.Causer interfaces (the former takes precedence).

Example
package main

import (
	"fmt"

	"emperror.dev/errors"
)

func main() {
	ErrSomething := errors.NewPlain("something")

	if err := errors.WithMessage(ErrSomething, "error"); err != nil {
		if errors.Unwrap(err) == ErrSomething {
			fmt.Println("something went wrong")
		} else {
			fmt.Println(err)
		}
	}

}
Output:

something went wrong

func UnwrapEach added in v0.3.0

func UnwrapEach(err error, fn func(err error) bool)

UnwrapEach loops through an error chain and calls a function for each of them.

The provided function can return false to break the loop before it reaches the end of the chain.

It supports both Go 1.13 errors.Wrapper and github.com/pkg/errors.Causer interfaces (the former takes precedence).

func WithDetails added in v0.4.0

func WithDetails(err error, details ...interface{}) error

WithDetails annotates err with with arbitrary key-value pairs.

func WithMessage

func WithMessage(err error, message string) error

WithMessage annotates err with a new message. If err is nil, WithMessage returns nil.

WithMessage is useful when the error already contains a stack trace, but adding additional info to the message helps in debugging.

Errors returned by WithMessage are formatted slightly differently:

%s	error messages separated by a colon and a space (": ")
%q	double-quoted error messages separated by a colon and a space (": ")
%v	one error message per line
%+v	one error message per line and stack trace (if any)

func WithMessagef

func WithMessagef(err error, format string, a ...interface{}) error

WithMessagef annotates err with the format specifier. If err is nil, WithMessagef returns nil.

WithMessagef is useful when the error already contains a stack trace, but adding additional info to the message helps in debugging.

The same formatting rules apply as in case of WithMessage.

func WithStack

func WithStack(err error) error

WithStack annotates err with a stack trace at the point WithStack was called. If err is nil, WithStack returns nil.

WithStack is commonly used with sentinel errors and errors returned from libraries not annotating errors with stack trace:

var ErrSomething = errors.NewPlain("something went wrong")

func doSomething() error {
	return errors.WithStack(ErrSomething)
}

func WithStackDepth

func WithStackDepth(err error, depth int) error

WithStackDepth annotates err with a stack trace at the given call depth. Zero identifies the caller of WithStackDepth itself. If err is nil, WithStackDepth returns nil.

WithStackDepth is generally used in other error constructors:

func MyWrapper(err error) error {
	return WithStackDepth(err, 1)
}

func WithStackDepthIf added in v0.2.0

func WithStackDepthIf(err error, depth int) error

WithStackDepthIf behaves the same way as WithStackDepth except it does not annotate the error with a stack trace if there is already one in err's chain.

func WithStackIf added in v0.2.0

func WithStackIf(err error) error

WithStackIf behaves the same way as WithStack except it does not annotate the error with a stack trace if there is already one in err's chain.

func Wrap

func Wrap(err error, message string) error

Wrap returns an error annotating err with a stack trace at the point Wrap is called, and the supplied message. If err is nil, Wrap returns nil.

Wrap is a shorthand for:

WithStack(WithMessage(err, message))

func WrapIf added in v0.2.0

func WrapIf(err error, message string) error

WrapIf behaves the same way as Wrap except it does not annotate the error with a stack trace if there is already one in err's chain.

If err is nil, WrapIf returns nil.

func WrapIfWithDetails added in v0.4.0

func WrapIfWithDetails(err error, message string, details ...interface{}) error

WrapIfWithDetails returns an error annotating err with a stack trace at the point WrapIfWithDetails is called, and the supplied message and details. If err is nil, WrapIfWithDetails returns nil.

WrapIfWithDetails is a shorthand for:

WithDetails(WithStackIf(WithMessage(err, message, details...))

func WrapIff added in v0.2.0

func WrapIff(err error, format string, a ...interface{}) error

WrapIff behaves the same way as Wrapf except it does not annotate the error with a stack trace if there is already one in err's chain.

If err is nil, WrapIff returns nil.

func WrapWithDetails added in v0.4.0

func WrapWithDetails(err error, message string, details ...interface{}) error

WrapWithDetails returns an error annotating err with a stack trace at the point WrapWithDetails is called, and the supplied message and details. If err is nil, WrapWithDetails returns nil.

WrapWithDetails is a shorthand for:

WithDetails(WithStack(WithMessage(err, message, details...))

func Wrapf

func Wrapf(err error, format string, a ...interface{}) error

Wrapf returns an error annotating err with a stack trace at the point Wrapf is called, and the format specifier. If err is nil, Wrapf returns nil.

Wrapf is a shorthand for:

WithStack(WithMessagef(err, format, a...))

Types

type Frame

type Frame = errors.Frame

Frame represents a program counter inside a stack frame. For historical reasons if Frame is interpreted as a uintptr its value represents the program counter + 1.

It is an alias of the same type in github.com/pkg/errors.

type Sentinel added in v0.4.3

type Sentinel string

Sentinel is a simple error without any annotated context, like stack trace. Useful for creating sentinel errors.

const ErrSomething = errors.Sentinel("something went wrong")

See https://dave.cheney.net/2016/04/07/constant-errors

Example

nolint: unused

package main

import (
	"emperror.dev/errors"
)

func main() {
	const ErrSomething = errors.Sentinel("something went wrong")

}
Output:

func (Sentinel) Error added in v0.4.3

func (e Sentinel) Error() string

type StackTrace

type StackTrace = errors.StackTrace

StackTrace is stack of Frames from innermost (newest) to outermost (oldest).

It is an alias of the same type in github.com/pkg/errors.

Directories

Path Synopsis
utils

Jump to

Keyboard shortcuts

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