• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

axkit / errors / 15131908489

20 May 2025 07:49AM UTC coverage: 91.009% (-0.2%) from 91.17%
15131908489

push

regorov
add migration guide

415 of 456 relevant lines covered (91.01%)

1.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.29
/errors.go
1
// Package errors provides a structured and extensible way to create, wrap, and manage errors
2
// in Go applications. It includes support for adding contextual information, managing error
3
// hierarchies, and setting attributes such as severity, HTTP status codes, and custom error codes.
4
//
5
// The package is designed to enhance error handling by allowing developers to attach additional
6
// metadata to errors, wrap underlying errors with more context, and facilitate debugging and
7
// logging. It also supports integration with alerting systems through the Alarm method.
8
//
9
// Key features include:
10
// - Wrapping errors with additional context.
11
// - Setting custom attributes like severity, status codes, and business codes.
12
// - Managing error stacks and hierarchies.
13
// - Sending alerts for critical errors.
14
// - Support for custom key-value pairs to enrich error information.
15
// - Integration with predefined error types for common scenarios.
16
// - Serialization errors for easy logging.
17
package errors
18

19
import (
20
        se "errors"
21
        "reflect"
22
)
23

24
// metadata holds the metadata for an error, including its message, severity level, etc.
25
type metadata struct {
26
        // message holds the final error's message.
27
        message string
28

29
        // severity holds the severity level of the error.
30
        severity SeverityLevel
31

32
        // statusCode holds the HTTP status code recommended for an HTTP response if specified.
33
        statusCode int
34

35
        // code holds the application-specific error code.
36
        code string
37

38
        // protected marks the error as protected to avoid certain types of modifications or exposure.
39
        protected bool
40
}
41

42
func (a *metadata) empty() bool {
1✔
43
        return a.message == "" &&
1✔
44
                a.severity == 0 &&
1✔
45
                a.statusCode == 0 && a.code == "" &&
1✔
46
                !a.protected
1✔
47
}
1✔
48

49
func (a *metadata) equal(b metadata) bool {
1✔
50
        return a.message == b.message &&
1✔
51
                a.severity == b.severity &&
1✔
52
                a.statusCode == b.statusCode && a.code == b.code &&
1✔
53
                a.protected == b.protected
1✔
54
}
1✔
55

56
// New creates and returns a standard Go error using the built-in errors.New function.
57
//
58
// This is implemented to maintain compatibility with existing Go error handling practices.
59
// However, the design philosophy of this package does not encourage the use of errors.New(msg)
60
// as commonly practiced in Go. Instead, it promotes the use of structured and enriched error
61
// handling mechanisms provided by this package.
62
func New(msg string) error {
1✔
63
        return se.New(msg)
1✔
64
}
1✔
65

66
// Wrap wraps an existing error with a new message, effectively creating
67
// a new error that includes the previous error.
68
func Wrap(err error, message string) *Error {
1✔
69
        var res Error
1✔
70

1✔
71
        if err != nil {
2✔
72
                res.err = err
1✔
73
                switch x := err.(type) {
1✔
74
                case *Error:
×
75
                        res = *x
×
76
                        x.pureWrapper = false
×
77
                case *ErrorTemplate:
1✔
78
                        res.metadata = x.metadata
1✔
79
                        res.fields = cloneMap(x.fields)
1✔
80
                case error:
1✔
81
                        break
1✔
82
                default:
×
83
                        break
×
84
                }
85
        }
86

87
        res.message = message
1✔
88
        if len(res.stack) == 0 {
2✔
89
                res.stack = CallerFramesFunc(1)
1✔
90
        }
1✔
91

92
        return &res
1✔
93
}
94

95
// Is checks if the error is of the same type as the target error.
96
func Is(err error, target error) bool {
1✔
97
        if err == target {
2✔
98
                return true
1✔
99
        }
1✔
100

101
        if err == nil || target == nil {
2✔
102
                return err == target
1✔
103
        }
1✔
104

105
        switch x := err.(type) {
1✔
106
        case *Error:
1✔
107
                return is(x, target)
1✔
108
        case *ErrorTemplate:
1✔
109
                return is(x.toError(), target)
1✔
110
        }
111

112
        return se.Is(err, target)
1✔
113
}
114

115
// is checks if two custom errors are equal based on their attributes or if their wrapped errors are equal.
116
func is(e *Error, target error) bool {
1✔
117
        switch t := target.(type) {
1✔
118
        case *Error:
1✔
119
                if t.pureWrapper {
2✔
120
                        return is(e, t.err)
1✔
121
                }
1✔
122
                if e.metadata.equal(t.metadata) || e == t {
2✔
123
                        return true
1✔
124
                }
1✔
125
        case *ErrorTemplate:
1✔
126
                if t.metadata.equal(e.metadata) {
2✔
127
                        return true
1✔
128
                }
1✔
129
        default:
1✔
130
                return se.Is(e.err, target)
1✔
131
        }
132

133
        if e.err == nil {
2✔
134
                return false
1✔
135
        }
1✔
136

137
        switch x := e.err.(type) {
1✔
138
        case *Error:
1✔
139
                return is(x, target)
1✔
140
        case *ErrorTemplate:
1✔
141
                return is(x.toError(), target)
1✔
142
        }
143

144
        return se.Is(e.err, target)
×
145
}
146

147
// As checks if the error can be cast to a target type.
148
func As(err error, target any) bool {
1✔
149
        if err == nil {
2✔
150
                return false
1✔
151
        }
1✔
152

153
        if target == nil {
2✔
154
                panic("axkit/errors: target cannot be nil")
1✔
155
        }
156

157
        if reflect.TypeOf(target).Kind() != reflect.Ptr {
1✔
158
                panic("axkit/errors: target must be a non-nil pointer")
×
159
        }
160

161
        if e, ok := err.(*Error); ok {
2✔
162
                return as(e, target)
1✔
163
        }
1✔
164

165
        if _, ok := err.(*ErrorTemplate); ok {
2✔
166
                panic("axkit/errors: error cannot be a pointer to a ErrorTemplate")
1✔
167
        }
168

169
        return se.As(err, target)
1✔
170
}
171

172
// as assists the As function in casting errors to the target type, accounting for wrapped errors.
173
func as(e *Error, target any) bool {
1✔
174

1✔
175
        switch t := target.(type) {
1✔
176
        case **Error:
1✔
177
                if e.metadata.equal((*t).metadata) || e == *t {
2✔
178
                        *t = e
1✔
179
                        return true
1✔
180
                }
1✔
181

182
                if e.err == nil {
1✔
183
                        return false
×
184
                }
×
185

186
                switch x := e.err.(type) {
1✔
187
                case *Error:
1✔
188
                        return as(x, target)
1✔
189
                case *ErrorTemplate:
×
190
                        if e.metadata.equal(x.metadata) {
×
191
                                *t = (*x).toError()
×
192
                                return true
×
193
                        }
×
194
                }
195
        case **ErrorTemplate, *ErrorTemplate:
1✔
196
                panic("axkit/errors: target cannot be a pointer to a PredefinedError")
1✔
197
        }
198

199
        return se.As(e, target)
×
200
}
201

202
func cloneMap(m map[string]any) map[string]any {
1✔
203
        if m == nil {
2✔
204
                return nil
1✔
205
        }
1✔
206

207
        clone := make(map[string]any, len(m))
1✔
208
        for k, v := range m {
2✔
209
                clone[k] = v
1✔
210
        }
1✔
211
        return clone
1✔
212
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc