errorex

package module
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Jan 7, 2021 License: MIT Imports: 2 Imported by: 10

README

ErrorEx

Package errorex implements the error interface with a custom error type that supports pre-formatting, inheritance, causes, custom data and extra error payloads.

It requires go1.13.

Example

Errors can be derived and derived errors will respond properly to errors.Is().

var (
	ErrApp       = New("command")
	ErrAppSub    = ErrApp.Wrap("method")
	ErrAppSubVar = ErrAppSub.WrapFormat("detail '%s'")

	ErrPkg       = New("package")
	ErrPkgSub    = ErrPkg.Wrap("method")
	ErrPkgSubVar = ErrPkgSub.WrapFormat("detail: '%s'")

	ErrMiddle       = New("middleware")
	ErrMiddleSub    = ErrMiddle.Wrap("method")
	ErrMiddleSubVar = ErrMiddleSub.WrapFormat("detail: '%s'")
)

type Middle struct{}

func (m *Middle) Bad() error {
	return ErrMiddleSubVar.WrapArgs("1337")
}

type Pkg struct {
	middle *Middle
}

func (p *Pkg) Bad() error {
	return ErrPkgSubVar.WrapCauseArgs(p.middle.Bad(), "69")
}

type App struct {
	pkg *Pkg
}

func (a *App) Bad() error {
	return ErrAppSubVar.WrapCauseArgs(a.pkg.Bad(), "42")
}

func TestMultiLevel(t *testing.T) {
	prog := &App{&Pkg{&Middle{}}}
	err := prog.Bad()
	if s := err.Error(); s != "command: method > detail '42' < package: method > detail: '69' < middleware: method > detail: '1337'" {
		t.Fatalf("Multilevel fail, want 'command: method > detail '42' < package: method > detail: '69' < middleware: method > detail: '1337'', got %s", s)
	}
}

ErrorEx can also carry an error that caused this error (retrievable by ErrorEx.Cause(), and custom data.

var (
	ErrBase       = errorex.New("mypackage")
	ErrUnmarshal  = ErrBase.WrapFormat("marshal error: '%s'")
	ErrInvalidPos = ErrBase.WrapFormat("invalid position: '%d:%d'")
)

func unmarshal(name string) error {

	data := ""
	if err := json.Unmarshal([]byte{}, data); err != nil {
		return ErrUnmarshal.WrapCauseArgs(err, name)
	}
	return nil
}

type ErrorData struct {
	Line   int
	Column int
}

func gotopos() error {
	return ErrInvalidPos.WrapDataArgs(&ErrorData{32, 64}, 32, 64)
}

func main() {
	err := unmarshal("MyValue")
	fmt.Println(err)
	// Outputs:
	// mypackage: marshal error: 'MyValue' < unexpected end of JSON input

	err = gotopos()
	fmt.Println(err)
	// Output:
	// mypackage: invalid position: '32:64'

	if eex, ok := (err).(*errorex.ErrorEx); ok {
		if data, ok := (eex.Datas()).(*ErrorData); ok {
			fmt.Println(data)
		}
	}
	// Output:
	// &{32 64}
}

If you have, say, some sort of a sink function that manages multiple objects that return errors you can use Extra() to store those errors and Extras() to retrieve them later.

err := New("base error")
err.Extra(New("Extra 1"))
err.Extra(New("Extra 2"))
err.Extra(New("Extra 3"))

// range over extra errors.
for _, e := range err.Extras() {
	_ = e
}

fmt.Println(err)
// Output: base error + Extra 1 + Extra 2 + Extra 3

Status

Work in progress, subject to change.

License

MIT, see the included LICENSE file.

Documentation

Overview

Package errorex provides additional error functionality.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Wrap added in v0.4.0

func Wrap(err error, message string) error

Wrap wraps an error with a message. If err is nil returns nil. If message is empty error is not wrapped.

func WrapCause added in v0.4.0

func WrapCause(err, cause error, message string) error

WrapCause wraps err with a message and appends the cause. If err is empty returns nil. If cause is nil, returns err wrapped with message. If message is empty err is not wrapped.

Types

type ErrorEx

type ErrorEx struct {
	// contains filtered or unexported fields
}

ErrorEx is an extended error type which provides utilities for error inheritance, causes, custom data payloads and extra errors. ErrorEx is not safe for concurrent use.

func New

func New(message string) *ErrorEx

New returns a new ErrorEx and sets its' message.

func NewFormat

func NewFormat(format string) (err *ErrorEx)

NewFormat returns a new ErrorEx and sets its text to a format string which will be used as a format string for errors deriving from it. Resulting error is used as a placeholder and will be skipped when printing but remains in the error chain and responds to Is() and As().

func (*ErrorEx) AnyData added in v0.3.1

func (ee *ErrorEx) AnyData() (data interface{})

AnyData returns first set data down the complete error wrap chain starting from this error. Errors not of ErrorEx type are skipped. If no set data is found result will be nil.

func (*ErrorEx) Cause

func (ee *ErrorEx) Cause() error

Cause returns the error that caused this error, which could be nil.

func (*ErrorEx) Data

func (ee *ErrorEx) Data() (data interface{})

Data returns this error data, which could be nil.

func (*ErrorEx) Error

func (ee *ErrorEx) Error() (message string)

Error implements the error interface.

It uses a custom printing scheme:

First error in the chain is always separated with a ':' from derived error messages. Wrapped errors are separated with a ';' if there are more than 3 wrap levels and the error is between 3rd and last level. Last error in the wrap stack is always separated with a '>' unless it directly wraps the base error in which case it is separated by ':'.

Example:

New("base").Wrap("sub1").Error()
Output: base: sub1

Example:

New("base").Wrap("sub1").Wrap("sub2").Error()
Output: base: sub1 > sub2

Example:

New("base").Wrap("sub1").Wrap("sub2").Wrap("sub3").Error()
Output: base: sub1; sub2 > sub3

Example:

New("base").Wrap("sub1").Wrap("sub2").Wrap("sub3").Wrap("sub4").Error()
Output: base: sub1; sub2; sub3 > sub4

Cause errors format the same way and are appended to final error after a '<' prefix.

Example:

New("base").WrapCause("error", New("cause"))
Output: base: error < cause

Extra errors carried by an error are appended and separated by ' + '

Example:

New("base").Wrap("sub").Extra(New("extra"))
Output: base: sub + extra

Errors created with NewFormat and WrapFormat are format placeholder errors and are not printed when printing the wrap chain.

Errors with an empty message are skipped when printing, regardless if they carry causes or extra errors.

func (*ErrorEx) Extra added in v0.3.0

func (ee *ErrorEx) Extra(err error) *ErrorEx

Extra appends an extra error to this error and returns self.

func (*ErrorEx) Extras added in v0.3.0

func (ee *ErrorEx) Extras() []error

Extras returns extra errors.

func (*ErrorEx) Is

func (ee *ErrorEx) Is(target error) bool

Is implements errors.Is(). Is returns true if either this or the cause error are siblings of target.

func (*ErrorEx) Unwrap

func (ee *ErrorEx) Unwrap() error

Unwrap implements error.Unwrap().

func (*ErrorEx) Wrap

func (ee *ErrorEx) Wrap(message string) *ErrorEx

Wrap wraps this error with a new error, sets new error message, then returns it.

func (*ErrorEx) WrapArgs added in v0.2.0

func (ee *ErrorEx) WrapArgs(args ...interface{}) *ErrorEx

WrapArgs derives a new error whose message will be formatted using specified args and this error message as a format string. WrapArgs should be used on errors which were constructed using NewFormat or WrapFormat using a format string as error message.

func (*ErrorEx) WrapCause added in v0.2.0

func (ee *ErrorEx) WrapCause(message string, err error) *ErrorEx

WrapCause returns a new derived ErrorEx that wraps a cause error. Calling errors.Is() on returned error returns true for target being the parent of either the returned error and the cause error that it wraps. Meaning:

ErrE := New("ErrA").Wrap("ErrB").WrapCause("ErrE", New("ErrC").Wrap("ErrD"))
errors.Is(ErrE, ErrA) // true
errors.Is(ErrE, ErrC) // true
fmt.Println(ErrF) // ErrA: ErrB > ErrC < ErrD: ErrE; ErrF

Derived ErrorEx unwraps to this error. Wrapped cause error is retrievable with Cause().

func (*ErrorEx) WrapCauseArgs added in v0.2.0

func (ee *ErrorEx) WrapCauseArgs(err error, args ...interface{}) *ErrorEx

WrapCauseArgs derives a new error which wraps a cause error and formats its error message from specified args and this error message as a format string. See WrapCause for more details.

func (*ErrorEx) WrapData added in v0.2.0

func (ee *ErrorEx) WrapData(message string, data interface{}) *ErrorEx

WrapData returns a new derived ErrorEx that wraps custom data.

func (*ErrorEx) WrapDataArgs added in v0.2.0

func (ee *ErrorEx) WrapDataArgs(data interface{}, args ...interface{}) *ErrorEx

WrapDataArgs derives a new error which wraps custom data and formats its error message from specified args and this error message as a format string. See WrapData for more details.

func (*ErrorEx) WrapDataFormat added in v0.3.1

func (ee *ErrorEx) WrapDataFormat(format string, data interface{}) *ErrorEx

WrapDataFormat wraps an error like WrapFormat but attatches data to it.

func (*ErrorEx) WrapFormat

func (ee *ErrorEx) WrapFormat(format string) (err *ErrorEx)

WrapFormat wraps this error with a new non-printable error whose message is a format string to errors further derived from it.

Resulting error can be formatted to a derived error with WrapArgs, WrapCauseArgs and WrapDataArgs.

The resulting error is skipped when printing the error chain but remains in the error chain and responds to Is() and As() properly.

Jump to

Keyboard shortcuts

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