Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
87a004e577
|
|||
376c6cab50
|
|||
4a3f25baa0
|
|||
aa33bc8df3
|
|||
96b3718375
|
@@ -7,6 +7,9 @@ type SyncSet[TData comparable] struct {
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// Add adds `value` to the set
|
||||
// returns true if the value was actually inserted
|
||||
// returns false if the value already existed
|
||||
func (s *SyncSet[TData]) Add(value TData) bool {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
@@ -15,10 +18,10 @@ func (s *SyncSet[TData]) Add(value TData) bool {
|
||||
s.data = make(map[TData]bool)
|
||||
}
|
||||
|
||||
_, ok := s.data[value]
|
||||
_, existsInPreState := s.data[value]
|
||||
s.data[value] = true
|
||||
|
||||
return !ok
|
||||
return !existsInPreState
|
||||
}
|
||||
|
||||
func (s *SyncSet[TData]) AddAll(values []TData) {
|
||||
|
@@ -270,8 +270,12 @@ func (b *Builder) Any(key string, val any) *Builder {
|
||||
}
|
||||
|
||||
func (b *Builder) Stringer(key string, val fmt.Stringer) *Builder {
|
||||
if val == nil {
|
||||
return b.addMeta(key, MDTString, "(!nil)")
|
||||
} else {
|
||||
return b.addMeta(key, MDTString, val.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) Stack() *Builder {
|
||||
return b.addMeta("@Stack", MDTString, string(debug.Stack()))
|
||||
|
@@ -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),
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package exerr
|
||||
|
||||
import (
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/dataext"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||
)
|
||||
|
||||
@@ -37,25 +38,32 @@ type ErrorType struct {
|
||||
|
||||
//goland:noinspection GoUnusedGlobalVariable
|
||||
var (
|
||||
TypeInternal = ErrorType{"INTERNAL_ERROR", langext.Ptr(500)}
|
||||
TypePanic = ErrorType{"PANIC", langext.Ptr(500)}
|
||||
TypeNotImplemented = ErrorType{"NOT_IMPLEMENTED", langext.Ptr(500)}
|
||||
TypeInternal = NewType("INTERNAL_ERROR", langext.Ptr(500))
|
||||
TypePanic = NewType("PANIC", langext.Ptr(500))
|
||||
TypeNotImplemented = NewType("NOT_IMPLEMENTED", langext.Ptr(500))
|
||||
|
||||
TypeWrap = ErrorType{"Wrap", nil}
|
||||
TypeWrap = NewType("Wrap", nil)
|
||||
|
||||
TypeBindFailURI = ErrorType{"BINDFAIL_URI", langext.Ptr(400)}
|
||||
TypeBindFailQuery = ErrorType{"BINDFAIL_QUERY", langext.Ptr(400)}
|
||||
TypeBindFailJSON = ErrorType{"BINDFAIL_JSON", langext.Ptr(400)}
|
||||
TypeBindFailFormData = ErrorType{"BINDFAIL_FORMDATA", langext.Ptr(400)}
|
||||
TypeBindFailHeader = ErrorType{"BINDFAIL_HEADER", langext.Ptr(400)}
|
||||
TypeBindFailURI = NewType("BINDFAIL_URI", langext.Ptr(400))
|
||||
TypeBindFailQuery = NewType("BINDFAIL_QUERY", langext.Ptr(400))
|
||||
TypeBindFailJSON = NewType("BINDFAIL_JSON", langext.Ptr(400))
|
||||
TypeBindFailFormData = NewType("BINDFAIL_FORMDATA", langext.Ptr(400))
|
||||
TypeBindFailHeader = NewType("BINDFAIL_HEADER", langext.Ptr(400))
|
||||
|
||||
TypeUnauthorized = ErrorType{"UNAUTHORIZED", langext.Ptr(401)}
|
||||
TypeAuthFailed = ErrorType{"AUTH_FAILED", langext.Ptr(401)}
|
||||
TypeUnauthorized = NewType("UNAUTHORIZED", langext.Ptr(401))
|
||||
TypeAuthFailed = NewType("AUTH_FAILED", langext.Ptr(401))
|
||||
|
||||
// other values come from pkgconfig
|
||||
)
|
||||
|
||||
var registeredTypes = dataext.SyncSet[string]{}
|
||||
|
||||
func NewType(key string, defStatusCode *int) ErrorType {
|
||||
insertOkay := registeredTypes.Add(key)
|
||||
if !insertOkay {
|
||||
panic("Cannot register same ErrType ('" + key + "') more than once")
|
||||
}
|
||||
|
||||
return ErrorType{key, defStatusCode}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
@@ -41,6 +46,36 @@ func (ee *ExErr) Is(e error) bool {
|
||||
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) {
|
||||
evt.Msg(ee.FormatLog(LogPrintFull))
|
||||
}
|
||||
@@ -196,6 +231,7 @@ func (ee *ExErr) RecursiveMeta(key string) *MetaValue {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Depth returns the depth of recursively contained errors
|
||||
func (ee *ExErr) Depth() int {
|
||||
if ee.OriginalError == nil {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
@@ -112,7 +112,7 @@ func (w *GinRouteBuilder) Use(middleware ...gin.HandlerFunc) *GinRouteBuilder {
|
||||
func (w *GinRouteBuilder) Handle(handler WHandlerFunc) {
|
||||
|
||||
if w.routes.wrapper.bufferBody {
|
||||
arr := make([]gin.HandlerFunc, len(w.handlers)+1)
|
||||
arr := make([]gin.HandlerFunc, 0, len(w.handlers)+1)
|
||||
arr = append(arr, BodyBuffer())
|
||||
arr = append(arr, w.handlers...)
|
||||
w.handlers = arr
|
||||
|
@@ -1,5 +1,5 @@
|
||||
package goext
|
||||
|
||||
const GoextVersion = "0.0.223"
|
||||
const GoextVersion = "0.0.228"
|
||||
|
||||
const GoextVersionTimestamp = "2023-08-08T11:52:40+0200"
|
||||
const GoextVersionTimestamp = "2023-08-08T15:33:52+0200"
|
||||
|
Reference in New Issue
Block a user