Wrapping errors with some useful functions.
It uses generic so Go1.18+ needed.
errwrap provides some functions that wrapping, asserting, checking an error. These functions are generated by Factory, so the errors can be wrapped more systemically.
The reason of using generic is to distinguish errors. Errors must be wrapped in different methods, and wrapping by their types make classifying easier.
First of all, you can make a basic error with using New function. errwrap provides 4 types of New functions, which are New, Newf, NewTyped, NewTypedf.
errwrap.New("new")
errwrap.Newf("%s%s", "new", "f")
errwrap.NewTyped[T]("newTyped")
errwrap.NewTypedf[T]("%s%s", "newTyped", "f")The error generated by one of these functions is able to be formatted like wrappedError which will be explained from now on.
Go error interface instances can be wrapped by this package's wrappedError. To wrap, you need to get a wrapper using a factory.
Factory needs a type as a generic which is representing where this error occured. And message is also needed, it explains what this error is.
wrapper, assertor := Factory[UserController]("user controller")
// format specifier supplied
wrapper, assertor := Factoryf[UserController]("%s controller", "user")Factory returns two functions which are wrapper and assertor. wrapper can wrap an error. The only thing you have to do to wrap is just inserting the error to the wrapper.
err = wrapper(err)When the error has a lot of wrapping depth, and when if you would like to pick a specified error that wrapped by this package, you can use assertor to do an assertion. Then assertor is going to return an asserted error and success or failure of the assertion. If it's failed, then the firstly returned argument will be nil.
To use assertor is same as wrapper.
asserted, ok := assertor(err)
fmt.Println(asserted) // user controller: ...checker works same as errors.Is, which checks a given error's type matching to the comparison's. To created this, wrapper is needed. wrapper becomes an argument of the function WithChecker, to create checker.
checker := WithChecker(&wrapper)After creating a checker, wrapper can make a checkable error. To use checker is like below:
if checker(err) {
fmt.Println("same")
}wrappedError stores an error stack, so it can be printed in detail using %+v or %+s.
fmt.Printf("%+v\n", err)
# this will be shown like
github.com/p9595jh/errwrap.TestChecker
/Users/medium/Desktop/PJH/blockchain/__xyz/2023.03/errwrap/errwrap_test.go:69
github.com/p9595jh/errwrap.TestChecker
/Users/medium/Desktop/PJH/blockchain/__xyz/2023.03/errwrap/errwrap_test.go:68
github.com/p9595jh/errwrap.TestChecker
/Users/medium/Desktop/PJH/blockchain/__xyz/2023.03/errwrap/errwrap_test.go:61
github.com/p9595jh/errwrap.TestChecker
/Users/medium/Desktop/PJH/blockchain/__xyz/2023.03/errwrap/errwrap_test.go:60Comparison with github.com/pkg/errors.
Test code:
func BenchmarkWrappedError(b *testing.B) {
w, _ := Factory[NONE]("test")
err := New("sample")
for i := 0; i < b.N; i++ {
err := err
for j := 0; j < 100; j++ {
err = w(err)
}
_ = err
}
}
func BenchmarkErrors(b *testing.B) {
err := errors.New("sample")
for i := 0; i < b.N; i++ {
err := err
for j := 0; j < 100; j++ {
err = errors.Wrap(err, "test")
}
_ = err
}
}Run:
$ go test -bench=. -benchtime=10s -benchmem -count 5
goos: darwin
goarch: amd64
pkg: github.com/p9595jh/errwrap
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkWrappedError-12 165193 72756 ns/op 28000 B/op 300 allocs/op
BenchmarkWrappedError-12 152595 72621 ns/op 28000 B/op 300 allocs/op
BenchmarkWrappedError-12 155252 73013 ns/op 28000 B/op 300 allocs/op
BenchmarkWrappedError-12 175780 72772 ns/op 28000 B/op 300 allocs/op
BenchmarkWrappedError-12 149059 71417 ns/op 28000 B/op 300 allocs/op
BenchmarkErrors-12 152004 82268 ns/op 33600 B/op 400 allocs/op
BenchmarkErrors-12 150874 78414 ns/op 33600 B/op 400 allocs/op
BenchmarkErrors-12 152422 79438 ns/op 33600 B/op 400 allocs/op
BenchmarkErrors-12 155005 85094 ns/op 33600 B/op 400 allocs/op
BenchmarkErrors-12 115872 88641 ns/op 33600 B/op 400 allocs/op
| Wrappable in 10s | ns/op | B/op | allocs/op | |
|---|---|---|---|---|
errwrap |
159575.8 | 72515.8 | 28000 | 300 |
github.com/pkg/errors |
145235.4 | 82771 | 33600 | 400 |
Processing ability of errwrap is improved around 9.87% than github.com/pkg/errors.