-
Notifications
You must be signed in to change notification settings - Fork 6
/
wrappedErrors.go
121 lines (105 loc) · 3.34 KB
/
wrappedErrors.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package errors
import (
"errors"
"fmt"
"runtime"
)
const skipStackLevels = 2
const nilPlaceholder = "<nil>"
type errorWithLocation struct {
err error
location string
}
type wrappableError struct {
errsWithLocation []errorWithLocation
}
// WrapError constructs a WrappableError from an error
func WrapError(err error) WrappableError {
errAsWrappable, ok := err.(WrappableError)
if ok {
return errAsWrappable
}
return &wrappableError{
errsWithLocation: []errorWithLocation{createErrorWithLocation(err, skipStackLevels)},
}
}
// WrapWithMessage wraps the target error with a new one, created using the input message
func (werr *wrappableError) WrapWithMessage(errMessage string) WrappableError {
return werr.wrapWithErrorWithSkipLevels(errors.New(errMessage), skipStackLevels+1)
}
// WrapWithStackTrace wraps the target error with a new one, without any message only a stack frame trace
func (werr *wrappableError) WrapWithStackTrace() WrappableError {
return werr.wrapWithErrorWithSkipLevels(errors.New(""), skipStackLevels+1)
}
// WrapWithError wraps the target error with the provided one
func (werr *wrappableError) WrapWithError(err error) WrappableError {
return werr.wrapWithErrorWithSkipLevels(err, skipStackLevels+1)
}
// GetBaseError gets the core error
func (werr *wrappableError) GetBaseError() error {
errorSlice := werr.errsWithLocation
return errorSlice[0].err
}
// GetLastError gets the last wrapped error
func (werr *wrappableError) GetLastError() error {
errorSlice := werr.errsWithLocation
return errorSlice[len(errorSlice)-1].err
}
func (werr *wrappableError) wrapWithErrorWithSkipLevels(err error, skipStackLevels int) *wrappableError {
newErrs := make([]errorWithLocation, len(werr.errsWithLocation))
copy(newErrs, werr.errsWithLocation)
if err == nil {
return &wrappableError{
errsWithLocation: newErrs,
}
}
return &wrappableError{
errsWithLocation: append(newErrs, createErrorWithLocation(err, skipStackLevels)),
}
}
func createErrorWithLocation(err error, skipStackLevels int) errorWithLocation {
_, file, line, _ := runtime.Caller(skipStackLevels)
locationLine := fmt.Sprintf("%s:%d", file, line)
errWithLocation := errorWithLocation{err: err, location: locationLine}
return errWithLocation
}
// Error is the standard error function implementation for wrappable errors
func (werr *wrappableError) Error() string {
strErr := ""
errorSlice := werr.errsWithLocation
for idxErr := range errorSlice {
errWithLocation := errorSlice[len(errorSlice)-1-idxErr]
errMsg := nilPlaceholder
if errWithLocation.err != nil {
errMsg = errWithLocation.err.Error()
}
suffix := ""
if errMsg != "" {
suffix = " [" + errMsg + "]"
}
strErr += "\t" + errWithLocation.location + suffix
if idxErr < len(errorSlice)-1 {
strErr += "\n"
}
}
return strErr
}
// Unwrap represents standard error function implementation for wrappable errors
func (werr *wrappableError) Unwrap() error {
wrappingErr := werr.errsWithLocation
if len(wrappingErr) == 1 {
return wrappingErr[0].err
}
return &wrappableError{
errsWithLocation: werr.errsWithLocation[:len(werr.errsWithLocation)-1],
}
}
// Is represents standard error function implementation for wrappable errors
func (werr *wrappableError) Is(target error) bool {
for _, err := range werr.errsWithLocation {
if errors.Is(err.err, target) {
return true
}
}
return false
}