-
Notifications
You must be signed in to change notification settings - Fork 0
/
errors.go
131 lines (118 loc) · 3.87 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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Package errors provides error management.
//
// By convention, wrapping functions return a nil error if the provided error is nil.
package errors
import (
std_errors "errors"
"runtime"
"strings"
"testing"
"github.com/pierrre/errors/errbase"
"github.com/pierrre/errors/errmsg"
"github.com/pierrre/errors/errstack"
)
// ErrUnsupported is an alias for [std_errors.ErrUnsupported].
var ErrUnsupported = std_errors.ErrUnsupported
// New returns a new error with a message and a stack.
//
// Warning: don't use this function to create a global (sentinel) error, as it will contain the stack of the (main) goroutine creating it.
// Use [errbase.New] instead.
func New(msg string) error {
err := errbase.New(msg)
err = errstack.WrapSkip(err, 1)
if ReportGlobalInit != nil {
checkGlobalInit(err, ReportGlobalInit)
}
return err //nolint: wrapcheck // The error is wrapped.
}
// Newf returns a new error with a formatted message and a stack.
//
// It supports the %w verb.
//
// Warning: don't use this function to create a global (sentinel) error, as it will contain the stack of the (main) goroutine creating it.
// Use [errbase.Newf] instead.
func Newf(format string, args ...any) error {
err := errbase.Newf(format, args...)
err = errstack.WrapSkip(err, 1)
if ReportGlobalInit != nil {
checkGlobalInit(err, ReportGlobalInit)
}
return err //nolint: wrapcheck // The error is wrapped.
}
// ReportGlobalInit reports a global error initialization.
// It is discouraged to call [New] or [Newf] to create a global (sentinel) error, as it will contain the stack of the (main) goroutine that created it.
// Instead, call [errbase.New] or [errbase.Newf], and [Wrap] it before returning it, which will add the stack of the goroutine returning the error.
//
// Example:
//
// var ErrGlobal = errbase.New("global error")
//
// func myFunc() error {
// return errors.Wrap(ErrGlobal, "myFunc error")
// }
//
// The default values's behavior is to panic during tests, and do nothing during normal execution.
// It can be disabled by setting it to nil.
//
// The implementation of [New] and [Newf] checks if the error is created by a function named "init".
// It doesn't report errors created by "init()" functions, which are named "init.N" where N is a number.
var ReportGlobalInit func(error) = func() func(error) {
var f func(error)
if testing.Testing() {
f = func(err error) {
panic(err)
}
}
return f
}()
func checkGlobalInit(err error, report func(error)) {
// This code doesn't call [errstack.Frames] to avoid memory allocations.
errf, ok := err.(interface {
StackFrames() []uintptr
})
if !ok {
return
}
pcs := errf.StackFrames()
if len(pcs) == 0 {
return
}
f := runtime.FuncForPC(pcs[0])
if !strings.HasSuffix(f.Name(), ".init") {
return
}
err = Wrap(err, "global error initialization detected, use errbase.New() instead, see https://pkg.go.dev/github.com/pierrre/errors#ReportGlobalInit ")
report(err)
}
// Wrap adds a message to an error, and a stack if it doesn't have one.
func Wrap(err error, msg string) error {
err = errstack.EnsureSkip(err, 1)
err = errmsg.Wrap(err, msg)
return err
}
// Wrapf adds a formatted message to an error, and a stack if it doesn't have one.
//
// It doesn't support the %w verb.
func Wrapf(err error, format string, args ...any) error {
err = errstack.EnsureSkip(err, 1)
err = errmsg.Wrapf(err, format, args...)
return err
}
// As is an alias for [std_errors.As].
func As(err error, target any) bool {
return std_errors.As(err, target)
}
// Is is an alias for [std_errors.Is].
func Is(err, target error) bool {
return std_errors.Is(err, target)
}
// Join calls [std_errors.Join] and adds a stack.
func Join(errs ...error) error {
err := std_errors.Join(errs...)
err = errstack.WrapSkip(err, 1)
return err //nolint:wrapcheck // The error is wrapped.
}
// Unwrap is an alias for [std_errors.Unwrap].
func Unwrap(err error) error {
return std_errors.Unwrap(err)
}