txerr

package
v0.0.8 Latest Latest
Warning

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

Go to latest
Published: Apr 3, 2020 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package txerr provides common functions for querying and formatting errors generated by txfile.

Creating errors in txfile for use with txerr must follow some common conventions, so to guarantee some level consistency among packages.

Package txerr expects errors to implement methods for querying error internals. This allows us to treat errors as interfaces, while having package specific implementations. A common set of recommended methods is documented with the Error type.

The Error type is designed to rely on builtin types only. No implementation must be forced to use a type define in txerr or any other common package.

Errors are treated as a tree of individual error values. Each node in the tree can have 0, 1 or N children. Errors capturing a single cause only must implement `Cause() error`. Errors capturing multiple causes must implement `Causes() []error`. An error is a Leaf-Node if: - It does not implement `Cause()` or `Causes()` - It implements `Cause()` and `err.Cause() == nil` - It implements `Causes()` and `len(err.Causes()) == 0`.

Each node in the tree provides addition context. The operation which caused the error (common format: `package-name/type-method`), an error kind for use by the application (query error type and recover), a custom error context and an user message.

Error kinds use type error, but must be uniquely comparable. That is due to interface comparison rules every package defining an error kind must define a custom type + constants for every supported error kind. Common pattern used within txfile:

// this type declaration ensures comparisons with values from other package
// will not get us false positives
type errKind int

//go:generate stringer -type=errKind -linecomment=true
const (
  Value1 errKind = iota // readable error message
  Value2                // another readable error message
)

func (k errKind) Error() string { // satisfy error interface
  return k.String()
}

Errors can have a package specific context. When creating a context, a nested error should not repeat the variables also present in the parent context. This requires us to implement some kind of context-merging when adding a cause to an error. Common pattern for error types used in txfile:

type Error struct {
  op string
  kind error
  cause error
  ctx errorCtx
  msg string
}

type errorCtx struct {
  k1 int
  k2 uint

  // optional context field (if you are too lazy to create a many error
  // types use bool or check for empty values)
  isK3 bool
  k3 string
}

// fulfill txerr.Error interface:
func (e *Error) Error() string   { return txerr.Report(e) } // implement `error` with help of txerr
func (e *Error) Op() string      { return e.op }
func (e *Error) Kind() error     { return e.kind }
func (e *Error) Cause() error    { return e.cause }
func (e *Error) Context() string { return e.ctx.String() }
func (e *Error) Message() string { return e.msg }

// implement Format and Errors to make some logging libs formatting errors
// based on these methods happy (e.g. zap).
func (e *Error) Format(s fmt.State, c rune) { txerr.Format(e, s, c) }
func (e *Error) Errors() []error {
  if e.cause == nil {
    return nil
  }
  return []error{e.cause}
}

// context based accessors (optional)
func (e *Error) K1() int { return e.ctx.k1 }

func (ctx *errorCtx) String() string {
  buf := &strbld.Builder{}
    buf.Fmt("k1=%v k2=%v", ctx.k1, ctx.k2)
    if ctx.isK3 {
      buf.Fmt(" k3=%v", ctx.k3)
    }
  return buf.String()
}

// optional error building helpers (e.g. for wrapping another error):
func (e *Error) of(kind errKind) *Error {
  e.kind = kind
  return e
}

func (e *Error) report(s string) *Error {
  e.msg = s
  return e
}

func (e *Error) causedBy(cause error) *Error {
  e.cause = cause
  if other, ok := cause.(*Error); ok {
    mergeCtx(e, other)
  }
  return e
}

func mergeCtx(err, cause *Error) { ... }

Objects very often cary context to be added to an error. In txfile these kind of objects provide additional helpers for constructing errors. These helpers guarantee that the context is correctly reported:

type myObj struct {...}

func (obj *myObj) errWrap(op string, cause error) *Error {
  return obj.err(op).causedBy(cause)
}

func (obj *myObj) err(op string) *Error {
  return &Error{op: op, ctx: errCtx()}
}

func (obj *myObj) errCtx() errorCtx {
  ...
}

All error construction should use the appropriate constructors:

func (obj *myObj) methodThatFails() error {
  const op = "<package-name>/obj-<readable-op>"

  ...

  // on failure
  if err != nil {
    return obj.errWrap(op, err).of(ForApplicationUse).report("custom user message")
  }

  ...
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FindErrWith

func FindErrWith(in error, pred func(err error) bool) error

FindErrWith returns the first error in the error tree, that matches the given predicate.

func FindKind

func FindKind(in error, kind error) error

FindKind returns the first error that matched `kind`.

func FindKindIf

func FindKindIf(in error, fn func(kind error) bool) error

FindKindIf returns the first error with a kind that fulfills the user predicate

func FindOp

func FindOp(in error, op string) error

FindOp returns the first error with the given `op` value.

func Format

func Format(err error, s fmt.State, c rune)

Format provides a common formatting implementation for adding the fmt.Formatter interface to custom errors. Usage:

func (e *myError) Format(s fmt.State, c rune) { txerr.Format(e, s, c) }

func GetKind

func GetKind(in error) error

GetKind returns the first error kind found in the error tree.

func GetOp

func GetOp(in error) string

GetOp returns the first errors it's Op value.

func Is

func Is(kind error, in error) bool

Is checks if any error in the error tree matches `kind`.

func IsOp

func IsOp(op string, in error) bool

IsOp checks if any error in the error tree is caused by `op`.

func Iter

func Iter(in error, fn func(err error) bool)

Iter iterates the complete error tree calling fn on each error value found. The user function fn can stop the iteration by returning false.

func Report

func Report(in error, verbose bool) string

Report formats a strings from an error value satisfying a subset of the Error interface. A multiline message will be generated if the error has nested errors and verbose is true.

Types

type Error

type Error interface {
	error

	// Op reports the failed operation.
	Op() string

	// Kind returns the error code, for checking and recovering from errors.
	Kind() error

	// Message reports an error message for users consumption.
	Message() string

	// Cause returns the cause of this error. If any.
	Cause() error
}

Error defines a common error interface type for use with txerr. The query and format function support a subset of Error being implemented, still it's recommended to implement at least the Error interface for use with txerr.

Jump to

Keyboard shortcuts

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