errs

package module
Version: v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Mar 17, 2022 License: MIT Imports: 3 Imported by: 347

README

errs

GoDoc Sourcegraph Go Report Card

errs is a package for making errors friendly and easy.

Creating Errors

The easiest way to use it, is to use the package level New function. It's much like fmt.Errorf, but better. For example:

func checkThing() error {
	return errs.New("what's up with %q?", "zeebo")
}

Why is it better? Errors come with a stack trace that is only printed when a "+" character is used in the format string. This should retain the benefits of being able to diagnose where and why errors happen, without all of the noise of printing a stack trace in every situation. For example:

func doSomeRealWork() {
	err := checkThing()
	if err != nil {
		fmt.Printf("%+v\n", err) // contains stack trace if it's a errs error.
		fmt.Printf("%v\n", err)  // does not contain a stack trace
		return
	}
}
Error Classes

You can create a Class of errors and check if any error was created by that class. The class name is prefixed to all of the errors it creates. For example:

var Unauthorized = errs.Class("unauthorized")

func checkUser(username, password string) error {
	if username != "zeebo" {
		return Unauthorized.New("who is %q?", username)
	}
	if password != "hunter2" {
		return Unauthorized.New("that's not a good password, jerkmo!")
	}
	return nil
}

func handleRequest() {
	if err := checkUser("zeebo", "hunter3"); Unauthorized.Has(err) {
		fmt.Println(err)
	}

	// output:
	// unauthorized: that's not a good password, jerkmo!
}

Classes can also Wrap other errors, and errors may be wrapped multiple times. For example:

var (
	Error        = errs.Class("mypackage")
	Unauthorized = errs.Class("unauthorized")
)

func deep3() error {
	return fmt.Errorf("ouch")
}

func deep2() error {
	return Unauthorized.Wrap(deep3())
}

func deep1() error {
	return Error.Wrap(deep2())
}

func deep() {
	fmt.Println(deep1())

	// output:
	// mypackage: unauthorized: ouch
}

In the above example, both Error.Has(deep1()) and Unauthorized.Has(deep1()) would return true, and the stack trace would only be recorded once at the deep2 call.

In addition, when an error has been wrapped, wrapping it again with the same class will not do anything. For example:

func doubleWrap() {
	fmt.Println(Error.Wrap(Error.New("foo")))

	// output:
	// mypackage: foo
}

This is to make it an easier decision if you should wrap or not (you should).

Utilities

Classes is a helper function to get a slice of classes that an error has. The latest wrap is first in the slice. For example:

func getClasses() {
	classes := errs.Classes(deep1())
	fmt.Println(classes[0] == &Error)
	fmt.Println(classes[1] == &Unauthorized)

	// output:
	// true
	// true
}

Finally, a helper function, Unwrap is provided to get the wrapped error in cases where you might want to inspect details. For example:

var Error = errs.Class("mypackage")

func getHandle() (*os.File, error) {
	fh, err := os.Open("neat_things")
	if err != nil {
		return nil, Error.Wrap(err)
	}
	return fh, nil
}

func checkForNeatThings() {
	fh, err := getHandle()
	if os.IsNotExist(errs.Unwrap(err)) {
		panic("no neat things?!")
	}
	if err != nil {
		panic("phew, at least there are neat things, even if i can't see them")
	}
	fh.Close()
}

It knows about both the Cause() error and Unwrap() error methods that are often used in the community, and will call them as many times as possible.

Defer

The package also provides WrapP versions of Wrap that are useful in defer contexts. For example:

func checkDefer() (err error) {
	defer Error.WrapP(&err)

	fh, err := os.Open("secret_stash")
	if err != nil {
		return nil, err
	}
	return fh.Close()
}
Groups

Groups allow one to collect a set of errors. For example:

func tonsOfErrors() error {
	var group errs.Group
	for _, work := range someWork {
		group.Add(maybeErrors(work))
	}
	return group.Err()
}

Some things to note:

  • The Add method only adds to the group if the passed in error is non-nil.
  • The Err method returns an error only if non-nil errors have been added, and additionally returns just the error if only one error was added. Thus, we always have that if you only call group.Add(err), then group.Err() == err.

The returned error will format itself similarly:

func groupFormat() {
	var group errs.Group
	group.Add(errs.New("first"))
	group.Add(errs.New("second"))
	err := group.Err()

	fmt.Printf("%v\n", err)
	fmt.Println()
	fmt.Printf("%+v\n", err)

	// output:
	// first; second
	//
	// group:
	// --- first
	//     ... stack trace
	// --- second
	//     ... stack trace
}
Contributing

errs is released under an MIT License. If you want to contribute, be sure to add yourself to the list in AUTHORS.

Documentation

Overview

Package errs provides a simple error package with stack traces.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Combine added in v1.1.0

func Combine(errs ...error) error

Combine combines multiple non-empty errors into a single error.

func Is added in v1.2.2

func Is(err, target error) bool

Is checks if any of the underlying errors matches target

func IsFunc added in v1.2.0

func IsFunc(err error, is func(err error) bool) bool

IsFunc checks if any of the underlying errors matches the func

func New

func New(format string, args ...interface{}) error

New returns an error not contained in any class. This is the same as calling fmt.Errorf(...) except it captures a stack trace on creation.

func Unwrap

func Unwrap(err error) error

Unwrap returns the underlying error, if any, or just the error.

func Wrap

func Wrap(err error) error

Wrap returns an error not contained in any class. It just associates a stack trace with the error. Wrap returns nil if err is nil.

func WrapP added in v1.2.0

func WrapP(err *error)

WrapP stores into the error pointer if it contains a non-nil error an error not contained in any class. It just associates a stack trace with the error. WrapP does nothing if the pointer or pointed at error is nil.

Types

type Causer added in v0.2.0

type Causer interface{ Cause() error }

Causer is implemented by all errors returned in this package. It returns the underlying cause of the error, or nil if there is no underlying cause.

type Class

type Class string

Class represents a class of errors. You can construct errors, and check if errors are part of the class.

func Classes

func Classes(err error) (classes []*Class)

Classes returns all the classes that have wrapped the error.

func (*Class) Has

func (c *Class) Has(err error) bool

Has returns true if the passed in error was wrapped by this class.

func (*Class) New

func (c *Class) New(format string, args ...interface{}) error

New constructs an error with the format string that will be contained by this class. This is the same as calling Wrap(fmt.Errorf(...)).

func (*Class) Wrap

func (c *Class) Wrap(err error) error

Wrap returns a new error based on the passed in error that is contained in this class. Wrap returns nil if err is nil.

func (*Class) WrapP added in v1.2.0

func (c *Class) WrapP(err *error)

WrapP stores into the error pointer if it contains a non-nil error an error contained in this class. WrapP does nothing if the pointer or pointed at error is nil.

type Group added in v1.1.0

type Group []error

Group is a list of errors.

func (*Group) Add added in v1.1.0

func (group *Group) Add(errs ...error)

Add adds non-empty errors to the Group.

func (Group) Err added in v1.1.0

func (group Group) Err() error

Err returns an error containing all of the non-nil errors. If there was only one error, it will return it. If there were none, it returns nil.

type Namer added in v0.2.0

type Namer interface{ Name() (string, bool) }

Namer is implemented by all errors returned in this package. It returns a name for the class of error it is, and a boolean indicating if the name is valid.

Source Files

Directories

Path Synopsis
Package errdata helps with associating some data to error classes
Package errdata helps with associating some data to error classes

Jump to

Keyboard shortcuts

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