errors

package module
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 21, 2024 License: BSD-2-Clause Imports: 6 Imported by: 2

README

Error module used internally at Primal Skill, inspired by upspin/errors and pkg/errors.

Docs

https://pkg.go.dev/github.com/primalskill/errors

Install

Get the module:

go get -u github.com/primalskill/errors

Import it in your code:

import "github.com/primalskill/errors"

Running the Tests

Navigate to the module's directory and execute:

make test

Example - Basic Usage

You can find more examples in the docs.

package main

import (
  "fmt"
  "github.com/primalskill/errors"
)

func main() {

  // Define an error
  err1 := errors.E(
    "some error", 
    errors.WithMeta(
      "metaKey1", "meta value", 
      "isAuth", true,
    ),
  )

  // Define another error and wrap err1
  err2 := errors.E(
    "wrapped error", 
    err1, 
    errors.WithMeta(
      "additionalMeta", 246,
    ),
  )

  fmt.Println(err2.Error()) // output: "wrapped error"

  // Convert stdlib error to errors.Error
  var ee *errors.Error
  errors.As(err2, &ee)
  fmt.Printf("%+v", ee.Meta) // output: [additionalMeta:246]

  // GetMeta helper func
  m, ok := errors.GetMeta(err1)
  fmt.Printf("\n%+v\n%+v\n", ok, m) // output: true \n [metaKey1:meta value isAuth:true]

  // Unwrap err2 to get err1
  uErr := errors.Unwrap(err2)
  fmt.Println(uErr.Error()) // output: "some error"
}

Example - Mirror an Existing Error

package main

import (
  "fmt"
  "github.com/primalskill/errors"
)

func main() {

  // Define an error
  err1 := errors.E("this is an error", errors.WithMeta("key1", "val1"))

  // Preload or "mirror" err1 in err2 with a few rules:
  // - err2 preloads err1 message
  // - err2 preloads err1 Meta key/value pairs
  // - if additional metas are defined on err2 it will merge it to the others
  // - err2 overwrites err1 source location to correctly show the location where err2 was executed
  err2 := errors.M(err1, errors.WithMeta("key2", "val2"))

  fmt.Printf("%+v", errors.PrettyPrint(err2)) // PrettyPrint should only be used in development to have a nicer output
  /* 
  output:

  this is an error
     |- Source : /goprograms/errors/example_test.go:20
    |- Meta :
      |- key1 : val1
      |- key2 : val2
  */
}

Documentation

Overview

Package errors implements custom error handling by wrapping the standard Error interface with additional meta data and simplifying the source (file:line) of each wrapped error.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func As

func As(err error, target any) bool

As finds the first error in err's chain that matches target, and if so, sets target to that error value and returns true. Otherwise, it returns false.

The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap.

An error matches target if the error's concrete value is assignable to the value pointed to by target, or if the error has a method As(any) bool such that As(target) returns true. In the latter case, the As method is responsible for setting target.

An error type might provide an As method so it can be treated as if it were a different error type.

As panics if target is not a non-nil pointer to either a type that implements error, or to any interface type.

func ContainsMessage added in v1.3.0

func ContainsMessage(err error, msg string) bool

ContainsMessage reports whether any error in err's chain contains msg. It performs a case-insensitive matching.

This is helpful when the error type's in err's chain is unknown and a string matching is preferred.

func E

func E(msg string, args ...any) error

E return a new error and sets the required msg argument as the error message. Additional arguments like a Meta map or another error can be passed into the function that will be set on the error.

Example
package main

import (
	"fmt"

	"github.com/primalskill/errors"
)

func main() {
	err := errors.E("this is an error")
	fmt.Println(err.Error())

}
Output:

this is an error
Example (Meta)
package main

import (
	"fmt"

	"github.com/primalskill/errors"
)

func main() {

	// err will carry a Meta map.
	err := errors.E("this is an error with meta", errors.WithMeta("myKey", "value_testing"))

	var e *errors.Error
	errors.As(err, &e)

	fmt.Printf("%+v", e.Meta)

}
Output:

[myKey:value_testing]

func HasMessage added in v1.3.0

func HasMessage(err error, msg string) bool

HasMessage reports whether any error in err's chain matches msg. It performs a case-insensitive matching.

This is helpful when the error type's in err's chain is unknown and a string matching is preferred.

func Is

func Is(err, target error) bool

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

The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap.

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.

An error type might provide an Is method so it can be treated as equivalent to an existing error. For example, if MyError defines

func (m MyError) Is(target error) bool { return target == fs.ErrExist }

then Is(MyError{}, fs.ErrExist) returns true. See syscall.Errno.Is for an example in the standard library.

func M added in v1.0.0

func M(err error, args ...any) error

M preloads err with all its Meta and wrapped errors if err is of type Error, otherwise it creates a new error of type Error and adds args on it. Passing in a regular error as err in the argument converts err to Error.

Example
package main

import (
	"fmt"

	"github.com/primalskill/errors"
)

func main() {
	err1 := errors.E("this is an error", errors.WithMeta("key1", "val1"))
	err2 := errors.M(err1, errors.WithMeta("key2", "val2"))

	fmt.Printf("%+v", errors.PrettyPrint(err2))
}

func MergeMeta

func MergeMeta(err error, m Meta) (bool, error)

MergeMeta will merge m to err.Meta if err is of type errors.Error and returns TRUE if the operation was successful, FALSE otherwise.

func PrettyPrint

func PrettyPrint(err error) string

PrettyPrint is a helper method to *Error.PrettyPrint. This should only be used in development.

Example
package main

import (
	"fmt"

	"github.com/primalskill/errors"
)

func main() {
	err := errors.E("my error", errors.WithMeta("key", "value"))
	fmt.Printf("%s", errors.PrettyPrint(err))
}

func Unwrap

func Unwrap(err error) error

Unwrap returns the result of calling the Unwrap method on err, if err's type contains an Unwrap method returning error. Otherwise, Unwrap returns nil.

Example
package main

import (
	"fmt"

	"github.com/primalskill/errors"
)

func main() {
	err1 := errors.E("error 1")
	err2 := errors.E("error 2", err1)

	e := errors.Unwrap(err2)
	fmt.Printf("%+v", e)

}
Output:

error 1

func With

func With(err error, args ...any) error

With is deprecated, see M. It is kept for backwards compatibility.

Types

type Error

type Error struct {
	Msg    string `json:"msg"`
	Source Source `json:"source,omitempty"`
	Meta   Meta   `json:"meta,omitempty"`
	// contains filtered or unexported fields
}

func Flatten

func Flatten(err error) (ret []Error)

Flatten returns a slice of Error from embedded err.

func (Error) As

func (e Error) As(target any) bool

func (*Error) Error

func (e *Error) Error() string

Error returns the error message and satisfies the stdlib Error interface.

func (Error) Is

func (e Error) Is(target error) bool

func (*Error) MarshalJSON added in v0.2.0

func (e *Error) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler for Error. Need to use a workaround for encoding because it will create an infinite loop if json.Marshal() is used in MarshalJSON().

func (*Error) PrettyPrint

func (e *Error) PrettyPrint() string

PrettyPrint will recursively print all embedded errors including all information on the error it can found. This should only be used in development.

Example
package main

import (
	"fmt"

	"github.com/primalskill/errors"
)

func main() {
	err := errors.E("my error", errors.WithMeta("key", "value"))

	var e *errors.Error
	errors.As(err, &e)

	fmt.Printf("%s", e.PrettyPrint())
}

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the error one level deep otherwise nil. This is a proxy method for Unwrap().

type Meta

type Meta map[string]any

Meta holds extra meta data around an error. Try adding simple values to the Meta map. Key order is not guaranteed.

func GetMeta

func GetMeta(err error) (Meta, bool)

GetMeta returns a Meta map or an empty Meta if the error doesn't contain a Meta or the error is not of type errors.Error. The second returned argument is TRUE if the err has a Meta, FALSE otherwise.

func WithMeta

func WithMeta(firstKey string, args ...any) Meta

WithMeta accepts an even number of arguments representing key/value pairs. The first argument "firstKey" forces the compiler to fail if the first argument is not a string. In "args" every odd argument must be of type string which will be used as the Meta map key. If an odd argument is not a string that pair will be skipped.

Example
package main

import (
	"fmt"

	"github.com/primalskill/errors"
)

func main() {
	// Valid
	mValid := errors.WithMeta("key1", 158, "key2", "some value", "anotherKey", true)
	fmt.Printf("%#v", mValid)

	// no value defined it will add !BADVALUE to noValueKey
	mBadValue := errors.WithMeta("noValueKey")
	fmt.Printf("\n%#v", mBadValue)

	// 16 is not a string for a key it will replace it with !BADKEY2
	mBadKey := errors.WithMeta("key1", "val1", 16, "val2", "key3", "val3")
	fmt.Printf("\n%#v", mBadKey)

	// 10 is not a string for a key it will replace it with !BADKEY2
	// key3 doesn't have a value it will add !BADVALUE to key3
	mBadKeyValue := errors.WithMeta("key1", "val1", 10, "val2", "key3")
	fmt.Printf("\n%#v", mBadKeyValue)
}

func (Meta) Merge

func (p Meta) Merge(firstKey string, args ...any) (m Meta)

Merge combines the arguments to an existing Meta and returns it. Existing keys will be overwritten.

Example
package main

import (
	"fmt"

	"github.com/primalskill/errors"
)

func main() {
	m := errors.WithMeta("key1", "val1")
	m = m.Merge("key2", "val2")

	fmt.Printf("%#v", m)

}
Output:

errors.Meta{"key1":"val1", "key2":"val2"}

func (Meta) Set

func (p Meta) Set(key string, value any) (m Meta)

Set will set key to value and returns Meta. Same keys will be overwritten.

func (Meta) String

func (p Meta) String() string

String returns Meta in [key1:val1 key2:val2 ...] format and satisfies the fmt.Stringer interface.

type Source added in v1.0.1

type Source string

Source represents an error source capturing the file path and line number where the error happened in the format <file path>:<line nunmber>. A Source is always attached to an error automatically.

Jump to

Keyboard shortcuts

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