Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
aa33bc8df3
|
|||
96b3718375
|
|||
5f9b55933b
|
|||
74d42637e7 |
@@ -269,6 +269,10 @@ func (b *Builder) Any(key string, val any) *Builder {
|
|||||||
return b.addMeta(key, MDTAny, newAnyWrap(val))
|
return b.addMeta(key, MDTAny, newAnyWrap(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Stringer(key string, val fmt.Stringer) *Builder {
|
||||||
|
return b.addMeta(key, MDTString, val.String())
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Builder) Stack() *Builder {
|
func (b *Builder) Stack() *Builder {
|
||||||
return b.addMeta("@Stack", MDTString, string(debug.Stack()))
|
return b.addMeta("@Stack", MDTString, string(debug.Stack()))
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,7 @@ func FromError(err error) *ExErr {
|
|||||||
StatusCode: nil,
|
StatusCode: nil,
|
||||||
Message: err.Error(),
|
Message: err.Error(),
|
||||||
WrappedErrType: fmt.Sprintf("%T", err),
|
WrappedErrType: fmt.Sprintf("%T", err),
|
||||||
|
WrappedErr: err,
|
||||||
Caller: "",
|
Caller: "",
|
||||||
OriginalError: nil,
|
OriginalError: nil,
|
||||||
Meta: getForeignMeta(err),
|
Meta: getForeignMeta(err),
|
||||||
@@ -43,6 +44,7 @@ func newExErr(cat ErrorCategory, errtype ErrorType, msg string) *ExErr {
|
|||||||
StatusCode: nil,
|
StatusCode: nil,
|
||||||
Message: msg,
|
Message: msg,
|
||||||
WrappedErrType: "",
|
WrappedErrType: "",
|
||||||
|
WrappedErr: nil,
|
||||||
Caller: callername(2),
|
Caller: callername(2),
|
||||||
OriginalError: nil,
|
OriginalError: nil,
|
||||||
Meta: make(map[string]MetaValue),
|
Meta: make(map[string]MetaValue),
|
||||||
@@ -59,6 +61,7 @@ func wrapExErr(e *ExErr, msg string, cat ErrorCategory, stacktraceskip int) *ExE
|
|||||||
StatusCode: e.StatusCode,
|
StatusCode: e.StatusCode,
|
||||||
Message: msg,
|
Message: msg,
|
||||||
WrappedErrType: "",
|
WrappedErrType: "",
|
||||||
|
WrappedErr: nil,
|
||||||
Caller: callername(1 + stacktraceskip),
|
Caller: callername(1 + stacktraceskip),
|
||||||
OriginalError: e,
|
OriginalError: e,
|
||||||
Meta: make(map[string]MetaValue),
|
Meta: make(map[string]MetaValue),
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/rs/xid"
|
"github.com/rs/xid"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -20,6 +21,7 @@ type ExErr struct {
|
|||||||
|
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
WrappedErrType string `json:"wrappedErrType"`
|
WrappedErrType string `json:"wrappedErrType"`
|
||||||
|
WrappedErr any `json:"-"`
|
||||||
Caller string `json:"caller"`
|
Caller string `json:"caller"`
|
||||||
|
|
||||||
OriginalError *ExErr `json:"originalError"`
|
OriginalError *ExErr `json:"originalError"`
|
||||||
@@ -33,6 +35,9 @@ func (ee *ExErr) Error() string {
|
|||||||
|
|
||||||
// Unwrap must be implemented so that some error.XXX methods work
|
// Unwrap must be implemented so that some error.XXX methods work
|
||||||
func (ee *ExErr) Unwrap() error {
|
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
|
return ee.OriginalError
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +46,36 @@ func (ee *ExErr) Is(e error) bool {
|
|||||||
return IsFrom(ee, e)
|
return IsFrom(ee, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// As must be implemented so that error.As(x) works
|
||||||
|
//
|
||||||
|
//goland:noinspection GoTypeAssertionOnErrors
|
||||||
|
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 {
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (ee *ExErr) Log(evt *zerolog.Event) {
|
func (ee *ExErr) Log(evt *zerolog.Event) {
|
||||||
evt.Msg(ee.FormatLog(LogPrintFull))
|
evt.Msg(ee.FormatLog(LogPrintFull))
|
||||||
}
|
}
|
||||||
@@ -196,6 +231,7 @@ func (ee *ExErr) RecursiveMeta(key string) *MetaValue {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Depth returns the depth of recursively contained errors
|
||||||
func (ee *ExErr) Depth() int {
|
func (ee *ExErr) Depth() int {
|
||||||
if ee.OriginalError == nil {
|
if ee.OriginalError == nil {
|
||||||
return 1
|
return 1
|
||||||
@@ -204,6 +240,59 @@ func (ee *ExErr) Depth() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// contains test if the supplied error is contained in this error (anywhere in the chain)
|
||||||
|
func (ee *ExErr) contains(original *ExErr) (*ExErr, bool) {
|
||||||
|
if original == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ee == original {
|
||||||
|
return ee, true
|
||||||
|
}
|
||||||
|
|
||||||
|
for curr := ee; curr != nil; curr = curr.OriginalError {
|
||||||
|
if curr.equalsDirectProperties(curr) {
|
||||||
|
return curr, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// equalsDirectProperties tests if ee and other are equals, but only looks at primary properties (not `OriginalError` or `Meta`)
|
||||||
|
func (ee *ExErr) equalsDirectProperties(other *ExErr) bool {
|
||||||
|
|
||||||
|
if ee.UniqueID != other.UniqueID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ee.Timestamp != other.Timestamp {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ee.Category != other.Category {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ee.Severity != other.Severity {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ee.Type != other.Type {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ee.StatusCode != other.StatusCode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ee.Message != other.Message {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ee.WrappedErrType != other.WrappedErrType {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ee.Caller != other.Caller {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func newID() string {
|
func newID() string {
|
||||||
return xid.New().String()
|
return xid.New().String()
|
||||||
}
|
}
|
||||||
|
93
exerr/exerr_test.go
Normal file
93
exerr/exerr_test.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
@@ -191,8 +191,8 @@ func Download(mimetype string, filepath string, filename string) HTTPResponse {
|
|||||||
return &fileHTTPResponse{mimetype: mimetype, filepath: filepath, filename: &filename}
|
return &fileHTTPResponse{mimetype: mimetype, filepath: filepath, filename: &filename}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DownloadData(mimetype string, filename string, data []byte) HTTPResponse {
|
func DownloadData(status int, mimetype string, filename string, data []byte) HTTPResponse {
|
||||||
return &downloadDataHTTPResponse{mimetype: mimetype, data: data, filename: &filename}
|
return &downloadDataHTTPResponse{statusCode: status, mimetype: mimetype, data: data, filename: &filename}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Redirect(sc int, newURL string) HTTPResponse {
|
func Redirect(sc int, newURL string) HTTPResponse {
|
||||||
|
2
go.mod
2
go.mod
@@ -21,7 +21,7 @@ require (
|
|||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.14.1 // indirect
|
github.com/go-playground/validator/v10 v10.15.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
2
go.sum
2
go.sum
@@ -26,6 +26,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
|||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
|
github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
|
||||||
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
|
github.com/go-playground/validator/v10 v10.15.0 h1:nDU5XeOKtB3GEa+uB7GNYwhVKsgjAR7VgKoNB6ryXfw=
|
||||||
|
github.com/go-playground/validator/v10 v10.15.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
package goext
|
package goext
|
||||||
|
|
||||||
const GoextVersion = "0.0.221"
|
const GoextVersion = "0.0.225"
|
||||||
|
|
||||||
const GoextVersionTimestamp = "2023-08-06T19:10:31+0200"
|
const GoextVersionTimestamp = "2023-08-08T13:09:15+0200"
|
||||||
|
Reference in New Issue
Block a user