Skip to content

Commit 78697d8

Browse files
committed
[patch] added helper function 'Type(err error)' of any error; returns errType(-1) if not of type *Error
[patch] added helper function 'HasType(err error, et errType)' to check if a provided errType is available anywhere in the wrapped errors
1 parent 6a36826 commit 78697d8

File tree

4 files changed

+178
-8
lines changed

4 files changed

+178
-8
lines changed

README.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Errors package is a drop-in replacement of the built-in Go errors package with n
1515
2. User friendly message
1616
3. File & line number prefixed to errors
1717
4. HTTP status code and user friendly message (wrapped messages are concatenated) for all error types
18+
5. Helper functions to generate each error type
19+
6. Helper function to get error Type, error type as int, check if error type is wrapped anywhere in chain
1820

1921
In case of nested errors, the messages (in case of nesting with this package's error) & errors are also looped through the full chain of errors.
2022

@@ -176,6 +178,7 @@ And output of the `fmt.Println(err.Error())`
176178
```bash
177179
/Users/username/go/src/errorscheck/main.go:28 /Users/username/go/src/errorscheck/main.go:20 sinking bar
178180
```
181+
179182
## Benchmark
180183

181184
Benchmark run on:
@@ -189,13 +192,16 @@ $ go test -bench=.
189192
goos: darwin
190193
goarch: amd64
191194
pkg: github.com/bnkamalesh/errors
192-
Benchmark_Internal-8 1880440 635 ns/op
193-
Benchmark_InternalErr-8 1607589 746 ns/op
194-
Benchmark_InternalGetError-8 1680831 711 ns/op
195-
Benchmark_InternalGetErrorWithNestedError-8 1462900 823 ns/op
196-
Benchmark_InternalGetMessage-8 1859172 646 ns/op
197-
Benchmark_InternalGetMessageWithNestedError-8 1649259 726 ns/op
198-
Benchmark_HTTPStatusCodeMessage-8 23497728 50.3 ns/op
195+
Benchmark_Internal-8 1874256 639 ns/op 368 B/op 5 allocs/op
196+
Benchmark_InternalErr-8 1612707 755 ns/op 368 B/op 5 allocs/op
197+
Benchmark_InternalGetError-8 1700966 706 ns/op 464 B/op 6 allocs/op
198+
Benchmark_InternalGetErrorWithNestedError-8 1458368 823 ns/op 464 B/op 6 allocs/op
199+
Benchmark_InternalGetMessage-8 1866562 643 ns/op 368 B/op 5 allocs/op
200+
Benchmark_InternalGetMessageWithNestedError-8 1656597 770 ns/op 400 B/op 6 allocs/op
201+
Benchmark_HTTPStatusCodeMessage-8 26003678 46.1 ns/op 16 B/op 1 allocs/op
202+
BenchmarkHasType-8 84689433 14.2 ns/op 0 B/op 0 allocs/op
203+
PASS
204+
ok github.com/bnkamalesh/errors 14.478s
199205
```
200206

201207
## Contributing

errors.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ func (e *Error) Error() string {
8282
return e.fileLine + " " + DefaultMessage
8383
}
8484

85-
// Message returns the user friendly message stored in the error struct
85+
// Message returns the user friendly message stored in the error struct. It will ignore all errors
86+
// which are not of type *Error
8687
func (e *Error) Message() string {
8788
messages := make([]string, 0, 5)
8889
messages = append(messages, e.message)

helper.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package errors
22

33
import (
4+
"errors"
45
"net/http"
56
"runtime"
67
"strconv"
@@ -179,3 +180,35 @@ func WriteHTTP(err error, w http.ResponseWriter) {
179180
w.WriteHeader(status)
180181
w.Write([]byte(msg))
181182
}
183+
184+
// Type returns the errType if it's an instance of *Error, -1 otherwise
185+
func Type(err error) errType {
186+
e, ok := err.(*Error)
187+
if !ok {
188+
return errType(-1)
189+
}
190+
return e.Type()
191+
}
192+
193+
// Type returns the errType as integer if it's an instance of *Error, -1 otherwise
194+
func TypeInt(err error) int {
195+
return Type(err).Int()
196+
}
197+
198+
// HasType will check if the provided err type is available anywhere nested in the error
199+
func HasType(err error, et errType) bool {
200+
if err == nil {
201+
return false
202+
}
203+
204+
e, _ := err.(*Error)
205+
if e == nil {
206+
return HasType(errors.Unwrap(err), et)
207+
}
208+
209+
if e.Type() == et {
210+
return true
211+
}
212+
213+
return HasType(e.Unwrap(), et)
214+
}

helper_test.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package errors
22

33
import (
44
"errors"
5+
"fmt"
56
"net/http"
67
"net/http/httptest"
78
"reflect"
@@ -643,3 +644,132 @@ func TestWriteHTTP(t *testing.T) {
643644
})
644645
}
645646
}
647+
648+
func TestHasCheck(t *testing.T) {
649+
type args struct {
650+
err error
651+
et errType
652+
}
653+
tests := []struct {
654+
name string
655+
args args
656+
want bool
657+
}{
658+
{
659+
name: "has required type, nested",
660+
args: args{
661+
err: ValidationErr(
662+
DuplicateErr(
663+
Internal("hello world"),
664+
DefaultMessage,
665+
),
666+
DefaultMessage,
667+
),
668+
et: TypeInternal,
669+
},
670+
want: true,
671+
},
672+
{
673+
name: "has required type, not nested",
674+
args: args{
675+
err: Internal("hello world"),
676+
et: TypeInternal,
677+
},
678+
want: true,
679+
},
680+
{
681+
name: "does not have required type",
682+
args: args{
683+
err: ValidationErr(
684+
DuplicateErr(
685+
Internal("hello world"),
686+
DefaultMessage,
687+
),
688+
DefaultMessage,
689+
),
690+
et: TypeInputBody,
691+
},
692+
want: false,
693+
},
694+
{
695+
name: "*Error wrapped in external error",
696+
args: args{
697+
err: fmt.Errorf("unknown error %w", Internal("internal error")),
698+
et: TypeInternal,
699+
},
700+
want: true,
701+
},
702+
{
703+
name: "other error type",
704+
args: args{
705+
err: fmt.Errorf("external error"),
706+
et: TypeInputBody,
707+
},
708+
want: false,
709+
},
710+
}
711+
for _, tt := range tests {
712+
t.Run(tt.name, func(t *testing.T) {
713+
if got := HasType(tt.args.err, tt.args.et); got != tt.want {
714+
t.Errorf("HasType() = %v, want %v %s", got, tt.want, tt.args.err.Error())
715+
}
716+
})
717+
}
718+
}
719+
720+
func BenchmarkHasType(b *testing.B) {
721+
err := ValidationErr(
722+
DuplicateErr(
723+
SubscriptionExpiredErr(
724+
ValidationErr(
725+
InputBodyErr(
726+
Internal("hello world"),
727+
DefaultMessage,
728+
),
729+
DefaultMessage,
730+
),
731+
DefaultMessage,
732+
),
733+
DefaultMessage,
734+
),
735+
DefaultMessage,
736+
)
737+
for i := 0; i < b.N; i++ {
738+
if !HasType(err, TypeInternal) {
739+
b.Fatal("TypeInternal not found")
740+
}
741+
}
742+
}
743+
744+
func TestTypeInt(t *testing.T) {
745+
type args struct {
746+
err error
747+
}
748+
tests := []struct {
749+
name string
750+
args args
751+
want int
752+
}{
753+
{
754+
name: "existing error type",
755+
args: args{
756+
err: Internal("internal error occurred"),
757+
},
758+
want: TypeInternal.Int(),
759+
},
760+
{
761+
name: "non-existent error type",
762+
args: args{
763+
err: fmt.Errorf("unknown error type"),
764+
},
765+
want: -1,
766+
},
767+
}
768+
for _, tt := range tests {
769+
t.Run(tt.name, func(t *testing.T) {
770+
if got := TypeInt(tt.args.err); got != tt.want {
771+
t.Errorf("TypeInt() = %v, want %v", got, tt.want)
772+
}
773+
})
774+
}
775+
}

0 commit comments

Comments
 (0)