-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherrors.go
More file actions
148 lines (133 loc) · 4.07 KB
/
Copy patherrors.go
File metadata and controls
148 lines (133 loc) · 4.07 KB
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// 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/errjoin"
"github.com/pierrre/errors/errmsg"
"github.com/pierrre/errors/errstack"
"github.com/pierrre/go-libs/syncutil/atomicutil"
)
// 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)
report := ReportGlobalInit.Load()
if report != nil {
checkGlobalInit(err, report)
}
return err
}
// 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)
report := ReportGlobalInit.Load()
if report != nil {
checkGlobalInit(err, report)
}
return err
}
// 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 value'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 atomicutil.Value[func(error)]
func init() {
var f func(error)
if testing.Testing() {
f = func(err error) {
panic(err)
}
}
ReportGlobalInit.Store(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 f == nil || !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 {
if err != nil {
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 {
if err != nil {
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)
}
// AsType is an alias for [std_errors.AsType].
func AsType[E error](err error) (E, bool) {
return std_errors.AsType[E](err)
}
// 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 := errjoin.Join(errs...)
if err != nil {
err = errstack.WrapSkip(err, 1)
}
return err
}
// Unwrap is an alias for [std_errors.Unwrap].
func Unwrap(err error) error {
return std_errors.Unwrap(err)
}