removed herror
This commit is contained in:
parent
b313767026
commit
4326fb11d6
|
@ -1,29 +0,0 @@
|
|||
package herror_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.sr.ht/~ewintr/go-kit/herror"
|
||||
)
|
||||
|
||||
var ErrTaskFailed = herror.New("task has failed")
|
||||
|
||||
func step() error {
|
||||
return fmt.Errorf("cannot move")
|
||||
}
|
||||
|
||||
func performTask() error {
|
||||
if err := step(); err != nil {
|
||||
return ErrTaskFailed.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Example() {
|
||||
if err := performTask(); err != nil {
|
||||
fmt.Print(err)
|
||||
return
|
||||
}
|
||||
// Output: task has failed
|
||||
//-> cannot move
|
||||
}
|
161
herror/herror.go
161
herror/herror.go
|
@ -1,161 +0,0 @@
|
|||
package herror
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Err represents an error
|
||||
type Err struct {
|
||||
error string
|
||||
wrapped *Err
|
||||
details string
|
||||
stack *Stacktrace
|
||||
}
|
||||
|
||||
type errJSON struct {
|
||||
E string `json:"error"`
|
||||
W *Err `json:"wrapped"`
|
||||
D string `json:"details"`
|
||||
S *Stacktrace `json:"stack"`
|
||||
}
|
||||
|
||||
// New returns a new instance for Err type with assigned error
|
||||
func New(err interface{}) *Err {
|
||||
newerror := new(Err)
|
||||
|
||||
switch e := err.(type) {
|
||||
case string:
|
||||
newerror.error = e
|
||||
|
||||
case error:
|
||||
if castErr, ok := e.(*Err); ok {
|
||||
return castErr
|
||||
}
|
||||
newerror.error = e.Error()
|
||||
}
|
||||
|
||||
return newerror
|
||||
}
|
||||
|
||||
// Wrap set an error that is wrapped by Err
|
||||
func Wrap(err, errwrapped error) error {
|
||||
newerr := New(err)
|
||||
return newerr.Wrap(errwrapped)
|
||||
}
|
||||
|
||||
// Unwrap returns a wrapped error if present
|
||||
func Unwrap(err error) error {
|
||||
return xerrors.Unwrap(err)
|
||||
}
|
||||
|
||||
// Is reports whether any error in err's chain matches target.
|
||||
func Is(err, target error) bool {
|
||||
return xerrors.Is(err, target)
|
||||
}
|
||||
|
||||
// Wrap set an error that is wrapped by Err
|
||||
func (e *Err) Wrap(err error) *Err {
|
||||
wrapped := New(err)
|
||||
|
||||
if deeper := xerrors.Unwrap(err); deeper != nil {
|
||||
Wrap(wrapped, deeper)
|
||||
}
|
||||
|
||||
newerr := &Err{
|
||||
error: e.error,
|
||||
wrapped: e.wrapped,
|
||||
details: e.details,
|
||||
stack: e.stack,
|
||||
}
|
||||
newerr.wrapped = wrapped
|
||||
return newerr
|
||||
}
|
||||
|
||||
// Unwrap returns wrapped error
|
||||
func (e *Err) Unwrap() error {
|
||||
if e.wrapped == nil {
|
||||
return nil
|
||||
}
|
||||
return e.wrapped
|
||||
}
|
||||
|
||||
// Is reports whether an error matches.
|
||||
func (e *Err) Is(err error) bool {
|
||||
if e.wrapped != nil {
|
||||
return e.error == err.Error() || e.wrapped.Is(err)
|
||||
}
|
||||
return e.error == err.Error()
|
||||
}
|
||||
|
||||
// CaptureStack sets stack traces when the method is called
|
||||
func (e *Err) CaptureStack() *Err {
|
||||
e.stack = NewStacktrace()
|
||||
return e
|
||||
}
|
||||
|
||||
// Stack returns full stack traces
|
||||
func (e *Err) Stack() *Stacktrace {
|
||||
return e.stack
|
||||
}
|
||||
|
||||
// AddDetails is a alias for AppendDetail [DEPRECATED]
|
||||
func (e *Err) AddDetails(v ...interface{}) *Err {
|
||||
return e.AppendDetails(v...)
|
||||
}
|
||||
|
||||
// AppendDetails appends formated variable information at the end of a text
|
||||
// field in the error mostly for debugging purposes.
|
||||
func (e *Err) AppendDetails(v ...interface{}) *Err {
|
||||
buff := new(bytes.Buffer)
|
||||
fmt.Fprintln(buff, e.details)
|
||||
spew.Fdump(buff, v...)
|
||||
e.details = buff.String()
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// Details returns error's details
|
||||
func (e *Err) Details() string {
|
||||
return e.details
|
||||
}
|
||||
|
||||
// Errors return a composed message of the assigned error e wrapped error
|
||||
func (e *Err) Error() string {
|
||||
if e.wrapped == nil {
|
||||
return e.error
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s\n-> %s", e.error, e.wrapped.Error())
|
||||
}
|
||||
|
||||
// UnmarshalJSON
|
||||
func (e *Err) UnmarshalJSON(b []byte) error {
|
||||
var errJSON errJSON
|
||||
if err := json.Unmarshal(b, &errJSON); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*e = Err{
|
||||
error: errJSON.E,
|
||||
wrapped: errJSON.W,
|
||||
details: errJSON.D,
|
||||
stack: errJSON.S,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON
|
||||
func (e *Err) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(errJSON{
|
||||
E: e.error,
|
||||
W: e.wrapped,
|
||||
D: e.details,
|
||||
S: e.stack,
|
||||
})
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
package herror_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"git.sr.ht/~ewintr/go-kit/herror"
|
||||
"git.sr.ht/~ewintr/go-kit/test"
|
||||
)
|
||||
|
||||
func TestHError(t *testing.T) {
|
||||
|
||||
t.Run("new error", func(t *testing.T) {
|
||||
errDefault := "this is an error"
|
||||
for _, tc := range []struct {
|
||||
m string
|
||||
input interface{}
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
m: "empty",
|
||||
},
|
||||
{
|
||||
m: "string",
|
||||
input: errDefault,
|
||||
expected: errDefault,
|
||||
},
|
||||
{
|
||||
m: "error",
|
||||
input: fmt.Errorf(errDefault),
|
||||
expected: errDefault,
|
||||
},
|
||||
{
|
||||
m: "herror.Err",
|
||||
input: herror.New(errDefault),
|
||||
expected: errDefault,
|
||||
},
|
||||
{
|
||||
m: "invalid type",
|
||||
input: 123456789,
|
||||
expected: "",
|
||||
},
|
||||
} {
|
||||
t.Run(tc.m, func(t *testing.T) {
|
||||
test.Equals(t, tc.expected, herror.New(tc.input).Error())
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("wrap", func(t *testing.T) {
|
||||
errmain := herror.New("MAIN ERROR")
|
||||
errfmt := fmt.Errorf("ERROR FORMATTED")
|
||||
errA := herror.New("ERR A")
|
||||
errB := herror.New("ERR B")
|
||||
errC := herror.New("ERR C")
|
||||
errD := herror.New("ERR D")
|
||||
errNested := errmain.Wrap(
|
||||
errA.Wrap(
|
||||
errB.Wrap(
|
||||
errC.Wrap(errD),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
for _, tc := range []struct {
|
||||
m string
|
||||
err error
|
||||
expected []error
|
||||
}{
|
||||
{
|
||||
m: "error",
|
||||
err: errfmt,
|
||||
expected: []error{
|
||||
errfmt,
|
||||
},
|
||||
},
|
||||
{
|
||||
m: "deeper nested wrap",
|
||||
err: errNested,
|
||||
expected: []error{
|
||||
errA, errB, errC, errD,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.m, func(t *testing.T) {
|
||||
newerr := errmain.Wrap(tc.err)
|
||||
|
||||
for _, e := range tc.expected {
|
||||
test.Equals(t, true, newerr.Is(e))
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("json marshalling", func(t *testing.T) {
|
||||
hError := herror.New("this is an error").
|
||||
Wrap(fmt.Errorf("this is another error")).
|
||||
CaptureStack()
|
||||
marshalled, err := json.Marshal(hError)
|
||||
test.OK(t, err)
|
||||
|
||||
var unmarshalled *herror.Err
|
||||
test.OK(t, json.Unmarshal(marshalled, &unmarshalled))
|
||||
test.Equals(t, hError, unmarshalled)
|
||||
})
|
||||
}
|
||||
|
||||
func ExampleErr_Wrap() {
|
||||
errA := herror.New("something went wrong")
|
||||
errB := fmt.Errorf("because of this error")
|
||||
newerr := herror.Wrap(errA, errB)
|
||||
|
||||
fmt.Print(herror.Unwrap(newerr), "\n", newerr)
|
||||
// Output: because of this error
|
||||
// something went wrong
|
||||
// -> because of this error
|
||||
}
|
||||
|
||||
func ExampleErr_Is() {
|
||||
errA := herror.New("something went wrong")
|
||||
errB := func() error {
|
||||
return errA
|
||||
}()
|
||||
|
||||
fmt.Print(herror.Is(errA, errB))
|
||||
// Output: true
|
||||
}
|
||||
|
||||
func ExampleErr_CaptureStack() {
|
||||
err := herror.New("something went wrong")
|
||||
err.CaptureStack()
|
||||
|
||||
fmt.Print(err, "\n", err.Stack().Frames[2].Function)
|
||||
// Output: something went wrong
|
||||
// ExampleErr_CaptureStack
|
||||
}
|
||||
|
||||
func ExampleErr_AddDetails() {
|
||||
err := herror.New("something went wrong")
|
||||
err.AddDetails(struct {
|
||||
number int
|
||||
}{123})
|
||||
|
||||
fmt.Print(err, err.Details())
|
||||
// Output: something went wrong
|
||||
// (struct { number int }) {
|
||||
// number: (int) 123
|
||||
// }
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
package herror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Stacktrace holds information about the frames of the stack.
|
||||
type Stacktrace struct {
|
||||
Frames []Frame `json:"frames,omitempty"`
|
||||
}
|
||||
|
||||
// Frame represents parsed information from runtime.Frame
|
||||
type Frame struct {
|
||||
Function string `json:"function,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Package string `json:"package,omitempty"`
|
||||
Filename string `json:"filename,omitempty"`
|
||||
AbsPath string `json:"abs_path,omitempty"`
|
||||
Line int `json:"line,omitempty"`
|
||||
InApp bool `json:"in_app,omitempty"`
|
||||
}
|
||||
|
||||
// FrameFilter represents function to filter frames
|
||||
type FrameFilter func(Frame) bool
|
||||
|
||||
const unknown string = "unknown"
|
||||
|
||||
// NewStacktrace creates a stacktrace using `runtime.Callers`.
|
||||
func NewStacktrace(filters ...FrameFilter) *Stacktrace {
|
||||
pcs := make([]uintptr, 100)
|
||||
n := runtime.Callers(1, pcs)
|
||||
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
frames := extractFrames(pcs[:n])
|
||||
|
||||
// default filter
|
||||
frames = filterFrames(frames, func(f Frame) bool {
|
||||
return f.Package == "runtime" || f.Package == "testing" ||
|
||||
strings.HasSuffix(f.Package, "/herror")
|
||||
})
|
||||
|
||||
for _, filter := range filters {
|
||||
frames = filterFrames(frames, filter)
|
||||
}
|
||||
|
||||
stacktrace := Stacktrace{
|
||||
Frames: frames,
|
||||
}
|
||||
|
||||
return &stacktrace
|
||||
}
|
||||
|
||||
// NewFrame assembles a stacktrace frame out of `runtime.Frame`.
|
||||
func NewFrame(f runtime.Frame) Frame {
|
||||
abspath := unknown
|
||||
filename := unknown
|
||||
if f.File != "" {
|
||||
abspath = f.File
|
||||
_, filename = filepath.Split(f.File)
|
||||
}
|
||||
|
||||
function := unknown
|
||||
pkgname := unknown
|
||||
typer := ""
|
||||
if f.Function != "" {
|
||||
pkgname, typer, function = deconstructFunctionName(f.Function)
|
||||
}
|
||||
|
||||
inApp := func() bool {
|
||||
out := strings.HasPrefix(abspath, build.Default.GOROOT) ||
|
||||
strings.Contains(pkgname, "vendor")
|
||||
return !out
|
||||
}()
|
||||
|
||||
return Frame{
|
||||
AbsPath: abspath,
|
||||
Filename: filename,
|
||||
Line: f.Line,
|
||||
Package: pkgname,
|
||||
Type: typer,
|
||||
Function: function,
|
||||
InApp: inApp,
|
||||
}
|
||||
}
|
||||
|
||||
func filterFrames(frames []Frame, filter FrameFilter) []Frame {
|
||||
filtered := make([]Frame, 0, len(frames))
|
||||
|
||||
for _, frame := range frames {
|
||||
if filter(frame) {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, frame)
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func extractFrames(pcs []uintptr) []Frame {
|
||||
frames := make([]Frame, 0, len(pcs))
|
||||
callersFrames := runtime.CallersFrames(pcs)
|
||||
|
||||
for {
|
||||
callerFrame, more := callersFrames.Next()
|
||||
frames = append([]Frame{
|
||||
NewFrame(callerFrame),
|
||||
}, frames...)
|
||||
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return frames
|
||||
}
|
||||
|
||||
func deconstructFunctionName(name string) (pkg string, typer string, function string) {
|
||||
if i := strings.LastIndex(name, "/"); i != -1 {
|
||||
pkg = name[:i]
|
||||
function = name[i+1:]
|
||||
|
||||
if d := strings.Index(function, "."); d != -1 {
|
||||
pkg = fmt.Sprint(pkg, "/", function[:d])
|
||||
function = function[d+1:]
|
||||
}
|
||||
|
||||
if o, c := strings.LastIndex(name, ".("), strings.LastIndex(name, ")."); o != -1 && c != -1 {
|
||||
pkg = name[:o]
|
||||
function = name[c+2:]
|
||||
|
||||
typer = name[o+2 : c]
|
||||
if i := strings.Index(typer, "*"); i != -1 {
|
||||
typer = typer[1:]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if i := strings.LastIndex(name, "."); i != -1 {
|
||||
pkg = name[:i]
|
||||
function = name[i+1:]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
package herror_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"git.sr.ht/~ewintr/go-kit/herror"
|
||||
"git.sr.ht/~ewintr/go-kit/test"
|
||||
)
|
||||
|
||||
func trace() *herror.Stacktrace {
|
||||
return herror.NewStacktrace()
|
||||
}
|
||||
|
||||
func traceStepIn(f []herror.FrameFilter) *herror.Stacktrace {
|
||||
return traceWithFilter(f)
|
||||
}
|
||||
|
||||
func traceWithFilter(f []herror.FrameFilter) *herror.Stacktrace {
|
||||
return herror.NewStacktrace(f...)
|
||||
}
|
||||
|
||||
func TestStacktrace(t *testing.T) {
|
||||
t.Run("new", func(t *testing.T) {
|
||||
stack := trace()
|
||||
|
||||
expectedFrames := []herror.Frame{
|
||||
herror.Frame{
|
||||
Function: "TestStacktrace.func1",
|
||||
},
|
||||
herror.Frame{
|
||||
Function: "trace",
|
||||
},
|
||||
}
|
||||
|
||||
test.Equals(t, len(expectedFrames), len(stack.Frames))
|
||||
for i, frame := range expectedFrames {
|
||||
test.Equals(t, frame.Function, stack.Frames[i].Function)
|
||||
test.Equals(t, "git.sr.ht/~ewintr/go-kit/herror_test", stack.Frames[i].Package)
|
||||
test.Equals(t, "stacktrace_test.go", stack.Frames[i].Filename)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("filter frames", func(t *testing.T) {
|
||||
|
||||
for _, tc := range []struct {
|
||||
m string
|
||||
filters []herror.FrameFilter
|
||||
expected []herror.Frame
|
||||
}{
|
||||
{
|
||||
m: "no filter",
|
||||
expected: []herror.Frame{
|
||||
herror.Frame{
|
||||
Function: "TestStacktrace.func2",
|
||||
},
|
||||
herror.Frame{
|
||||
Function: "traceStepIn",
|
||||
},
|
||||
herror.Frame{
|
||||
Function: "traceWithFilter",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
m: "single filter",
|
||||
expected: []herror.Frame{
|
||||
herror.Frame{
|
||||
Function: "traceStepIn",
|
||||
},
|
||||
herror.Frame{
|
||||
Function: "traceWithFilter",
|
||||
},
|
||||
},
|
||||
filters: []herror.FrameFilter{
|
||||
func(f herror.Frame) bool {
|
||||
return f.Function == "TestStacktrace.func2"
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
m: "multiple filters",
|
||||
expected: []herror.Frame{
|
||||
herror.Frame{
|
||||
Function: "traceWithFilter",
|
||||
},
|
||||
},
|
||||
filters: []herror.FrameFilter{
|
||||
func(f herror.Frame) bool {
|
||||
return f.Function == "TestStacktrace.func2"
|
||||
},
|
||||
func(f herror.Frame) bool {
|
||||
return f.Function == "traceStepIn"
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
stack := traceStepIn(tc.filters)
|
||||
|
||||
t.Run(tc.m, func(t *testing.T) {
|
||||
test.Equals(t, len(tc.expected), len(stack.Frames))
|
||||
|
||||
for i, frame := range tc.expected {
|
||||
test.Equals(t, frame.Function, stack.Frames[i].Function)
|
||||
test.Equals(t, "git.sr.ht/~ewintr/go-kit/herror_test", stack.Frames[i].Package)
|
||||
test.Equals(t, "stacktrace_test.go", stack.Frames[i].Filename)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestFrame(t *testing.T) {
|
||||
t.Run("new", func(t *testing.T) {
|
||||
f := func() herror.Frame {
|
||||
pc := make([]uintptr, 1)
|
||||
n := runtime.Callers(0, pc)
|
||||
test.Assert(t, n == 1, "expected available pcs")
|
||||
|
||||
frames := runtime.CallersFrames(pc)
|
||||
runtimeframe, _ := frames.Next()
|
||||
return herror.NewFrame(runtimeframe)
|
||||
}
|
||||
|
||||
frame := f()
|
||||
test.Equals(t, "Callers", frame.Function)
|
||||
test.Equals(t, "runtime", frame.Package)
|
||||
test.Equals(t, "extern.go", frame.Filename)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue