Compare commits
	
		
			6 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 376c6cab50 | |||
| 4a3f25baa0 | |||
| aa33bc8df3 | |||
| 96b3718375 | |||
| 5f9b55933b | |||
| 74d42637e7 | 
| @@ -7,6 +7,9 @@ type SyncSet[TData comparable] struct { | |||||||
| 	lock sync.Mutex | 	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 { | func (s *SyncSet[TData]) Add(value TData) bool { | ||||||
| 	s.lock.Lock() | 	s.lock.Lock() | ||||||
| 	defer s.lock.Unlock() | 	defer s.lock.Unlock() | ||||||
| @@ -15,10 +18,10 @@ func (s *SyncSet[TData]) Add(value TData) bool { | |||||||
| 		s.data = make(map[TData]bool) | 		s.data = make(map[TData]bool) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	_, ok := s.data[value] | 	_, existsInPreState := s.data[value] | ||||||
| 	s.data[value] = true | 	s.data[value] = true | ||||||
|  |  | ||||||
| 	return !ok | 	return !existsInPreState | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *SyncSet[TData]) AddAll(values []TData) { | func (s *SyncSet[TData]) AddAll(values []TData) { | ||||||
|   | |||||||
| @@ -269,6 +269,14 @@ 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 { | ||||||
|  | 	if val == nil { | ||||||
|  | 		return b.addMeta(key, MDTString, "(!nil)") | ||||||
|  | 	} else { | ||||||
|  | 		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), | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package exerr | package exerr | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"gogs.mikescher.com/BlackForestBytes/goext/dataext" | ||||||
| 	"gogs.mikescher.com/BlackForestBytes/goext/langext" | 	"gogs.mikescher.com/BlackForestBytes/goext/langext" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -37,25 +38,32 @@ type ErrorType struct { | |||||||
|  |  | ||||||
| //goland:noinspection GoUnusedGlobalVariable | //goland:noinspection GoUnusedGlobalVariable | ||||||
| var ( | var ( | ||||||
| 	TypeInternal       = ErrorType{"INTERNAL_ERROR", langext.Ptr(500)} | 	TypeInternal       = NewType("INTERNAL_ERROR", langext.Ptr(500)) | ||||||
| 	TypePanic          = ErrorType{"PANIC", langext.Ptr(500)} | 	TypePanic          = NewType("PANIC", langext.Ptr(500)) | ||||||
| 	TypeNotImplemented = ErrorType{"NOT_IMPLEMENTED", 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)} | 	TypeBindFailURI      = NewType("BINDFAIL_URI", langext.Ptr(400)) | ||||||
| 	TypeBindFailQuery    = ErrorType{"BINDFAIL_QUERY", langext.Ptr(400)} | 	TypeBindFailQuery    = NewType("BINDFAIL_QUERY", langext.Ptr(400)) | ||||||
| 	TypeBindFailJSON     = ErrorType{"BINDFAIL_JSON", langext.Ptr(400)} | 	TypeBindFailJSON     = NewType("BINDFAIL_JSON", langext.Ptr(400)) | ||||||
| 	TypeBindFailFormData = ErrorType{"BINDFAIL_FORMDATA", langext.Ptr(400)} | 	TypeBindFailFormData = NewType("BINDFAIL_FORMDATA", langext.Ptr(400)) | ||||||
| 	TypeBindFailHeader   = ErrorType{"BINDFAIL_HEADER", langext.Ptr(400)} | 	TypeBindFailHeader   = NewType("BINDFAIL_HEADER", langext.Ptr(400)) | ||||||
|  |  | ||||||
| 	TypeUnauthorized = ErrorType{"UNAUTHORIZED", langext.Ptr(401)} | 	TypeUnauthorized = NewType("UNAUTHORIZED", langext.Ptr(401)) | ||||||
| 	TypeAuthFailed   = ErrorType{"AUTH_FAILED", langext.Ptr(401)} | 	TypeAuthFailed   = NewType("AUTH_FAILED", langext.Ptr(401)) | ||||||
|  |  | ||||||
| 	// other values come from pkgconfig | 	// other values come from pkgconfig | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | var registeredTypes = dataext.SyncSet[string]{} | ||||||
|  |  | ||||||
| func NewType(key string, defStatusCode *int) ErrorType { | 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} | 	return ErrorType{key, defStatusCode} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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.227" | ||||||
|  |  | ||||||
| const GoextVersionTimestamp = "2023-08-06T19:10:31+0200" | const GoextVersionTimestamp = "2023-08-08T15:28:29+0200" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user