diff --git a/exerr/constructor.go b/exerr/constructor.go index 2b66dab..b4444fc 100644 --- a/exerr/constructor.go +++ b/exerr/constructor.go @@ -27,6 +27,7 @@ func FromError(err error) *ExErr { StatusCode: nil, Message: err.Error(), WrappedErrType: fmt.Sprintf("%T", err), + WrappedErr: err, Caller: "", OriginalError: nil, Meta: getForeignMeta(err), @@ -43,6 +44,7 @@ func newExErr(cat ErrorCategory, errtype ErrorType, msg string) *ExErr { StatusCode: nil, Message: msg, WrappedErrType: "", + WrappedErr: nil, Caller: callername(2), OriginalError: nil, Meta: make(map[string]MetaValue), @@ -59,6 +61,7 @@ func wrapExErr(e *ExErr, msg string, cat ErrorCategory, stacktraceskip int) *ExE StatusCode: e.StatusCode, Message: msg, WrappedErrType: "", + WrappedErr: nil, Caller: callername(1 + stacktraceskip), OriginalError: e, Meta: make(map[string]MetaValue), diff --git a/exerr/exerr.go b/exerr/exerr.go index 0471d20..a6dd2e6 100644 --- a/exerr/exerr.go +++ b/exerr/exerr.go @@ -4,6 +4,7 @@ import ( "github.com/rs/xid" "github.com/rs/zerolog" "gogs.mikescher.com/BlackForestBytes/goext/langext" + "reflect" "strings" "time" ) @@ -20,6 +21,7 @@ type ExErr struct { Message string `json:"message"` WrappedErrType string `json:"wrappedErrType"` + WrappedErr any `json:"-"` Caller string `json:"caller"` OriginalError *ExErr `json:"originalError"` @@ -33,6 +35,9 @@ func (ee *ExErr) Error() string { // Unwrap must be implemented so that some error.XXX methods work func (ee *ExErr) Unwrap() error { + if ee.OriginalError == nil { + return nil // this is neccessary - otherwise we return a wrapped nil and the `x == nil` comparison fails (= panic in errors.Is and other failures) + } return ee.OriginalError } @@ -44,17 +49,29 @@ func (ee *ExErr) Is(e error) bool { // As must be implemented so that error.As(x) works // //goland:noinspection GoTypeAssertionOnErrors -func (ee *ExErr) As(e any) bool { - if dstErr, ok := e.(*ExErr); ok { +func (ee *ExErr) As(target any) bool { + if dstErr, ok := target.(*ExErr); ok { + if dst0, ok := ee.contains(dstErr); ok { dstErr = dst0 return true } else { return false } - } else if dstErr, ok := e.(error); ok { - return IsFrom(ee, dstErr) + } else { + + val := reflect.ValueOf(target) + + typStr := val.Type().Elem().String() + + for curr := ee; curr != nil; curr = curr.OriginalError { + if curr.Category == CatForeign && curr.WrappedErrType == typStr && curr.WrappedErr != nil { + val.Elem().Set(reflect.ValueOf(curr.WrappedErr)) + return true + } + } + return false } } diff --git a/exerr/exerr_test.go b/exerr/exerr_test.go new file mode 100644 index 0000000..ee98351 --- /dev/null +++ b/exerr/exerr_test.go @@ -0,0 +1,93 @@ +package exerr + +import ( + "errors" + "gogs.mikescher.com/BlackForestBytes/goext/tst" + "testing" +) + +type golangErr struct { + Message string +} + +func (g golangErr) Error() string { + return g.Message +} + +type golangErr2 struct { + Message string +} + +func (g golangErr2) Error() string { + return g.Message +} + +type simpleError struct { +} + +func (g simpleError) Error() string { + return "Something simple went wroong" +} + +type simpleError2 struct { +} + +func (g simpleError2) Error() string { + return "Something simple went wroong" +} + +func TestExErrIs1(t *testing.T) { + e0 := simpleError{} + + wrap := Wrap(e0, "something went wrong").Str("test", "123").Build() + + tst.AssertTrue(t, errors.Is(wrap, simpleError{})) + tst.AssertFalse(t, errors.Is(wrap, golangErr{})) + tst.AssertFalse(t, errors.Is(wrap, golangErr{"error1"})) +} + +func TestExErrIs2(t *testing.T) { + e0 := golangErr{"error1"} + + wrap := Wrap(e0, "something went wrong").Str("test", "123").Build() + + tst.AssertTrue(t, errors.Is(wrap, e0)) + tst.AssertTrue(t, errors.Is(wrap, golangErr{"error1"})) + tst.AssertFalse(t, errors.Is(wrap, golangErr{"error2"})) + tst.AssertFalse(t, errors.Is(wrap, simpleError{})) +} + +func TestExErrAs(t *testing.T) { + + e0 := golangErr{"error1"} + + w0 := Wrap(e0, "something went wrong").Str("test", "123").Build() + + { + out := golangErr{} + ok := errors.As(w0, &out) + tst.AssertTrue(t, ok) + tst.AssertEqual(t, out.Message, "error1") + } + + w1 := Wrap(w0, "outher error").Build() + + { + out := golangErr{} + ok := errors.As(w1, &out) + tst.AssertTrue(t, ok) + tst.AssertEqual(t, out.Message, "error1") + } + + { + out := golangErr2{} + ok := errors.As(w1, &out) + tst.AssertFalse(t, ok) + } + + { + out := simpleError2{} + ok := errors.As(w1, &out) + tst.AssertFalse(t, ok) + } +} diff --git a/goextVersion.go b/goextVersion.go index 988f493..1f5760e 100644 --- a/goextVersion.go +++ b/goextVersion.go @@ -1,5 +1,5 @@ package goext -const GoextVersion = "0.0.224" +const GoextVersion = "0.0.225" -const GoextVersionTimestamp = "2023-08-08T12:38:22+0200" +const GoextVersionTimestamp = "2023-08-08T13:09:15+0200"