-
Notifications
You must be signed in to change notification settings - Fork 25
/
errors.go
104 lines (86 loc) · 2.9 KB
/
errors.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
package errors
import (
"fmt"
goerrors "github.com/go-errors/errors"
"github.com/urfave/cli/v2"
)
// Errorf creates a new error and wraps in an Error type that contains the stack trace.
func Errorf(message string, args ...interface{}) error {
err := fmt.Errorf(message, args...)
return goerrors.Wrap(err, 1)
}
// If this error is returned, the program should exit with the given exit code.
type ErrorWithExitCode struct {
Err error
ExitCode int
}
func (err ErrorWithExitCode) Error() string {
return err.Err.Error()
}
// Wrap the given error in an Error type that contains the stack trace. If the given error already has a stack trace,
// it is used directly. If the given error is nil, return nil.
func WithStackTrace(err error) error {
if err == nil {
return nil
}
return goerrors.Wrap(err, 1)
}
// Wrap the given error in an Error type that contains the stack trace and has the given message prepended as part of
// the error message. If the given error already has a stack trace, it is used directly. If the given error is nil,
// return nil.
func WithStackTraceAndPrefix(err error, message string, args ...interface{}) error {
if err == nil {
return nil
}
return goerrors.WrapPrefix(err, fmt.Sprintf(message, args...), 1)
}
// Returns true if actual is the same type of error as expected. This method unwraps the given error objects (if they
// are wrapped in objects with a stacktrace) and then does a simple equality check on them.
func IsError(actual error, expected error) bool {
return goerrors.Is(actual, expected)
}
// If the given error is a wrapper that contains a stacktrace, unwrap it and return the original, underlying error.
// In all other cases, return the error unchanged
func Unwrap(err error) error {
if err == nil {
return nil
}
goError, isGoError := err.(*goerrors.Error)
if isGoError {
return goError.Err
}
return err
}
// Convert the given error to a string, including the stack trace if available
func PrintErrorWithStackTrace(err error) string {
if err == nil {
return ""
}
switch underlyingErr := err.(type) {
case *goerrors.Error:
return underlyingErr.ErrorStack()
default:
return err.Error()
}
}
// A method that tries to recover from panics, and if it succeeds, calls the given onPanic function with an error that
// explains the cause of the panic. This function should only be called from a defer statement.
func Recover(onPanic func(cause error)) {
if rec := recover(); rec != nil {
err, isError := rec.(error)
if !isError {
err = fmt.Errorf("%v", rec)
}
onPanic(WithStackTrace(err))
}
}
// Use this to wrap every command you add to *cli.App to handle panics by logging them with a stack trace and returning
// an error up the chain.
func WithPanicHandling(action func(c *cli.Context) error) func(c *cli.Context) error {
return func(context *cli.Context) (err error) {
defer Recover(func(cause error) {
err = cause
})
return action(context)
}
}