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

axkit / errors / 15113607802

19 May 2025 12:57PM UTC coverage: 91.17% (-0.9%) from 92.063%
15113607802

push

regorov
refactoring

192 of 221 new or added lines in 5 files covered. (86.88%)

3 existing lines in 1 file now uncovered.

413 of 453 relevant lines covered (91.17%)

1.03 hits per line

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

86.96
/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
// Wrap wraps an existing error with a new message, effectively creating
57
// a new error that includes the previous error.
58
func Wrap(err error, message string) *Error {
1✔
59
        var res Error
1✔
60

1✔
61
        if err != nil {
2✔
62
                res.err = err
1✔
63
                switch x := err.(type) {
1✔
NEW
64
                case *Error:
×
NEW
65
                        res = *x
×
NEW
66
                        x.pureWrapper = false
×
67
                case *ErrorTemplate:
1✔
68
                        res.metadata = x.metadata
1✔
69
                        res.fields = cloneMap(x.fields)
1✔
70
                case error:
1✔
71
                        break
1✔
NEW
72
                default:
×
NEW
73
                        break
×
74
                }
75
        }
76

77
        res.message = message
1✔
78
        if len(res.stack) == 0 {
2✔
79
                res.stack = CallerFramesFunc(1)
1✔
80
        }
1✔
81

82
        return &res
1✔
83
}
84

85
// Is checks if the error is of the same type as the target error.
86
func Is(err error, target error) bool {
1✔
87
        if err == target {
2✔
88
                return true
1✔
89
        }
1✔
90

91
        if err == nil || target == nil {
2✔
92
                return err == target
1✔
93
        }
1✔
94

95
        switch x := err.(type) {
1✔
96
        case *Error:
1✔
97
                return is(x, target)
1✔
98
        case *ErrorTemplate:
1✔
99
                return is(x.toError(), target)
1✔
100
        }
101

102
        return se.Is(err, target)
1✔
103
}
104

105
// is checks if two custom errors are equal based on their attributes or if their wrapped errors are equal.
106
func is(e *Error, target error) bool {
1✔
107
        switch t := target.(type) {
1✔
108
        case *Error:
1✔
109
                if t.pureWrapper {
2✔
110
                        return is(e, t.err)
1✔
111
                }
1✔
112
                if e.metadata.equal(t.metadata) || e == t {
2✔
113
                        return true
1✔
114
                }
1✔
115
        case *ErrorTemplate:
1✔
116
                if t.metadata.equal(e.metadata) {
2✔
117
                        return true
1✔
118
                }
1✔
119
        default:
1✔
120
                return se.Is(e.err, target)
1✔
121
        }
122

123
        if e.err == nil {
2✔
124
                return false
1✔
125
        }
1✔
126

127
        switch x := e.err.(type) {
1✔
128
        case *Error:
1✔
129
                return is(x, target)
1✔
130
        case *ErrorTemplate:
1✔
131
                return is(x.toError(), target)
1✔
132
        }
133

134
        return se.Is(e.err, target)
×
135
}
136

137
// As checks if the error can be cast to a target type.
138
func As(err error, target any) bool {
1✔
139
        if err == nil {
2✔
140
                return false
1✔
141
        }
1✔
142

143
        if target == nil {
2✔
144
                panic("axkit/errors: target cannot be nil")
1✔
145
        }
146

147
        if reflect.TypeOf(target).Kind() != reflect.Ptr {
1✔
NEW
148
                panic("axkit/errors: target must be a non-nil pointer")
×
149
        }
150

151
        if e, ok := err.(*Error); ok {
2✔
152
                return as(e, target)
1✔
153
        }
1✔
154

155
        if _, ok := err.(*ErrorTemplate); ok {
2✔
156
                panic("axkit/errors: error cannot be a pointer to a ErrorTemplate")
1✔
157
        }
158

159
        return se.As(err, target)
1✔
160
}
161

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

1✔
165
        switch t := target.(type) {
1✔
166
        case **Error:
1✔
167
                if e.metadata.equal((*t).metadata) || e == *t {
2✔
168
                        *t = e
1✔
169
                        return true
1✔
170
                }
1✔
171

172
                if e.err == nil {
1✔
173
                        return false
×
174
                }
×
175

176
                switch x := e.err.(type) {
1✔
177
                case *Error:
1✔
178
                        return as(x, target)
1✔
NEW
179
                case *ErrorTemplate:
×
NEW
180
                        if e.metadata.equal(x.metadata) {
×
181
                                *t = (*x).toError()
×
182
                                return true
×
183
                        }
×
184
                }
185
        case **ErrorTemplate, *ErrorTemplate:
1✔
186
                panic("axkit/errors: target cannot be a pointer to a PredefinedError")
1✔
187
        }
188

189
        return se.As(e, target)
×
190
}
191

192
func cloneMap(m map[string]any) map[string]any {
1✔
193
        if m == nil {
2✔
194
                return nil
1✔
195
        }
1✔
196

197
        clone := make(map[string]any, len(m))
1✔
198
        for k, v := range m {
2✔
199
                clone[k] = v
1✔
200
        }
1✔
201
        return clone
1✔
202
}
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