Skip to content

Commit 41d12e1

Browse files
committed
Complete interface definitions for errors
Add test and fix missing definition Signed-off-by: Derek McGowan <[email protected]>
1 parent 70440b8 commit 41d12e1

3 files changed

Lines changed: 81 additions & 10 deletions

File tree

errors.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,20 +139,28 @@ type errAlreadyExists struct{}
139139

140140
func (errAlreadyExists) Error() string { return "already exists" }
141141

142+
func (errAlreadyExists) AlreadyExists() {}
143+
142144
func (e errAlreadyExists) WithMessage(msg string) error {
143145
return customMessage{e, msg}
144146
}
145147

148+
type alreadyExists interface {
149+
AlreadyExists()
150+
}
151+
146152
// IsAlreadyExists returns true if the error is due to an already existing
147153
// metadata item
148154
func IsAlreadyExists(err error) bool {
149-
return errors.Is(err, ErrAlreadyExists)
155+
return errors.Is(err, ErrAlreadyExists) || isInterface[alreadyExists](err)
150156
}
151157

152158
type errPermissionDenied struct{}
153159

154160
func (errPermissionDenied) Error() string { return "permission denied" }
155161

162+
func (errPermissionDenied) Forbidden() {}
163+
156164
func (e errPermissionDenied) WithMessage(msg string) error {
157165
return customMessage{e, msg}
158166
}
@@ -172,28 +180,40 @@ type errResourceExhausted struct{}
172180

173181
func (errResourceExhausted) Error() string { return "resource exhausted" }
174182

183+
func (errResourceExhausted) ResourceExhausted() {}
184+
175185
func (e errResourceExhausted) WithMessage(msg string) error {
176186
return customMessage{e, msg}
177187
}
178188

189+
type resourceExhausted interface {
190+
ResourceExhausted()
191+
}
192+
179193
// IsResourceExhausted returns true if the error is due to
180194
// a lack of resources or too many attempts.
181195
func IsResourceExhausted(err error) bool {
182-
return errors.Is(err, errResourceExhausted{})
196+
return errors.Is(err, errResourceExhausted{}) || isInterface[resourceExhausted](err)
183197
}
184198

185199
type errFailedPrecondition struct{}
186200

187201
func (e errFailedPrecondition) Error() string { return "failed precondition" }
188202

203+
func (errFailedPrecondition) FailedPrecondition() {}
204+
189205
func (e errFailedPrecondition) WithMessage(msg string) error {
190206
return customMessage{e, msg}
191207
}
192208

209+
type failedPrecondition interface {
210+
FailedPrecondition()
211+
}
212+
193213
// IsFailedPrecondition returns true if an operation could not proceed due to
194214
// the lack of a particular condition
195215
func IsFailedPrecondition(err error) bool {
196-
return errors.Is(err, errFailedPrecondition{})
216+
return errors.Is(err, errFailedPrecondition{}) || isInterface[failedPrecondition](err)
197217
}
198218

199219
type errConflict struct{}
@@ -242,27 +262,39 @@ type errAborted struct{}
242262

243263
func (errAborted) Error() string { return "aborted" }
244264

265+
func (errAborted) Aborted() {}
266+
245267
func (e errAborted) WithMessage(msg string) error {
246268
return customMessage{e, msg}
247269
}
248270

271+
type aborted interface {
272+
Aborted()
273+
}
274+
249275
// IsAborted returns true if an operation was aborted.
250276
func IsAborted(err error) bool {
251-
return errors.Is(err, errAborted{})
277+
return errors.Is(err, errAborted{}) || isInterface[aborted](err)
252278
}
253279

254280
type errOutOfRange struct{}
255281

256282
func (errOutOfRange) Error() string { return "out of range" }
257283

284+
func (errOutOfRange) OutOfRange() {}
285+
258286
func (e errOutOfRange) WithMessage(msg string) error {
259287
return customMessage{e, msg}
260288
}
261289

290+
type outOfRange interface {
291+
OutOfRange()
292+
}
293+
262294
// IsOutOfRange returns true if an operation could not proceed due
263295
// to data being out of the expected range.
264296
func IsOutOfRange(err error) bool {
265-
return errors.Is(err, errOutOfRange{})
297+
return errors.Is(err, errOutOfRange{}) || isInterface[outOfRange](err)
266298
}
267299

268300
type errNotImplemented struct{}

errors_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package errdefs
1919
import (
2020
"context"
2121
"errors"
22+
"fmt"
2223
"reflect"
2324
"testing"
2425
)
@@ -158,6 +159,39 @@ func TestWithMessage(t *testing.T) {
158159
}
159160
}
160161

162+
func TestInterfaceMatch(t *testing.T) {
163+
testCases := []struct {
164+
err error
165+
check func(error) bool
166+
}{
167+
{ErrUnknown, isInterface[unknown]},
168+
{ErrInvalidArgument, isInterface[invalidParameter]},
169+
{ErrNotFound, isInterface[notFound]},
170+
{ErrAlreadyExists, isInterface[alreadyExists]},
171+
{ErrPermissionDenied, isInterface[forbidden]},
172+
{ErrResourceExhausted, isInterface[resourceExhausted]},
173+
{ErrFailedPrecondition, isInterface[failedPrecondition]},
174+
{ErrConflict, isInterface[conflict]},
175+
{ErrNotModified, isInterface[notModified]},
176+
{ErrAborted, isInterface[aborted]},
177+
{ErrOutOfRange, isInterface[outOfRange]},
178+
{ErrNotImplemented, isInterface[notImplemented]},
179+
{ErrInternal, isInterface[system]},
180+
{ErrUnavailable, isInterface[unavailable]},
181+
{ErrDataLoss, isInterface[dataLoss]},
182+
{ErrUnauthenticated, isInterface[unauthorized]},
183+
}
184+
185+
for _, tc := range testCases {
186+
tc := tc
187+
t.Run(fmt.Sprintf("%T", tc.err), func(t *testing.T) {
188+
if !tc.check(tc.err) {
189+
t.Fatal("Error does not match interface")
190+
}
191+
})
192+
}
193+
}
194+
161195
type customInvalidArgument struct{}
162196

163197
func (*customInvalidArgument) Error() string {

resolve.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,22 @@ func firstError(err error) error {
7474
return ErrInvalidArgument
7575
case notFound:
7676
return ErrNotFound
77-
// Skip ErrAlreadyExists, no interface defined
77+
case alreadyExists:
78+
return ErrAlreadyExists
7879
case forbidden:
7980
return ErrPermissionDenied
80-
// Skip ErrResourceExhasuted, no interface defined
81-
// Skip ErrFailedPrecondition, no interface defined
81+
case resourceExhausted:
82+
return ErrResourceExhausted
83+
case failedPrecondition:
84+
return ErrFailedPrecondition
8285
case conflict:
8386
return ErrConflict
8487
case notModified:
8588
return ErrNotModified
86-
// Skip ErrAborted, no interface defined
87-
// Skip ErrOutOfRange, no interface defined
89+
case aborted:
90+
return ErrAborted
91+
case errOutOfRange:
92+
return ErrOutOfRange
8893
case notImplemented:
8994
return ErrNotImplemented
9095
case system:

0 commit comments

Comments
 (0)