diff options
Diffstat (limited to 'vendor/codeberg.org/gruf/go-errors')
-rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/README.md | 9 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/build_caller.go | 26 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/build_nocaller.go | 18 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/build_notrace.go | 20 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/build_trace.go | 27 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/callers.go | 98 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/error.go | 35 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/error_notrace.go | 33 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/errors.go | 231 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/once.go | 36 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/runtime.go | 97 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/standard.go | 221 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/value.go | 52 |
13 files changed, 546 insertions, 357 deletions
diff --git a/vendor/codeberg.org/gruf/go-errors/v2/README.md b/vendor/codeberg.org/gruf/go-errors/v2/README.md index 90049abec..21f7cb767 100644 --- a/vendor/codeberg.org/gruf/go-errors/v2/README.md +++ b/vendor/codeberg.org/gruf/go-errors/v2/README.md @@ -1,5 +1,8 @@ # go-errors -simple but powerful errors library that allows easy wrapping and stacktracing of errors. - -to disable stacktraces set the `notrace` build tag.
\ No newline at end of file +powerful errors library with a simple API that allows: +- accessing all the standard library errors functions +- wrapping of errors +- adding values to errors, in a similar manner to contexts +- including calling function prefix when tag=errcaller is set +- including stacktrace with error when tag=errtrace is set diff --git a/vendor/codeberg.org/gruf/go-errors/v2/build_caller.go b/vendor/codeberg.org/gruf/go-errors/v2/build_caller.go new file mode 100644 index 000000000..ad5067574 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-errors/v2/build_caller.go @@ -0,0 +1,26 @@ +//go:build errcaller +// +build errcaller + +package errors + +import ( + _ "unsafe" +) + +// IncludesCaller is a compile-time flag used to indicate whether +// to include calling function prefix on error wrap / creation. +const IncludesCaller = true + +type caller string + +// set will set the actual caller value +// only when correct build flag is set. +func (c *caller) set(v string) { + *c = caller(v) +} + +// value returns the actual caller value +// only when correct build flag is set +func (c caller) value() string { + return string(c) +} diff --git a/vendor/codeberg.org/gruf/go-errors/v2/build_nocaller.go b/vendor/codeberg.org/gruf/go-errors/v2/build_nocaller.go new file mode 100644 index 000000000..935283bf5 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-errors/v2/build_nocaller.go @@ -0,0 +1,18 @@ +//go:build !errcaller +// +build !errcaller + +package errors + +// IncludesCaller is a compile-time flag used to indicate whether +// to include calling function prefix on error wrap / creation. +const IncludesCaller = false + +type caller struct{} + +// set will set the actual caller value +// only when correct build flag is set. +func (caller) set(string) {} + +// value returns the actual caller value +// only when correct build flag is set. +func (caller) value() string { return "" } diff --git a/vendor/codeberg.org/gruf/go-errors/v2/build_notrace.go b/vendor/codeberg.org/gruf/go-errors/v2/build_notrace.go new file mode 100644 index 000000000..acee2e698 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-errors/v2/build_notrace.go @@ -0,0 +1,20 @@ +//go:build !errtrace +// +build !errtrace + +package errors + +import "runtime" + +// IncludesStacktrace is a compile-time flag used to indicate +// whether to include stacktraces on error wrap / creation. +const IncludesStacktrace = false + +type trace struct{} + +// set will set the actual trace value +// only when correct build flag is set. +func (trace) set([]runtime.Frame) {} + +// value returns the actual trace value +// only when correct build flag is set. +func (trace) value() Callers { return nil } diff --git a/vendor/codeberg.org/gruf/go-errors/v2/build_trace.go b/vendor/codeberg.org/gruf/go-errors/v2/build_trace.go new file mode 100644 index 000000000..e7b652699 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-errors/v2/build_trace.go @@ -0,0 +1,27 @@ +//go:build errtrace +// +build errtrace + +package errors + +import ( + "runtime" + _ "unsafe" +) + +// IncludesStacktrace is a compile-time flag used to indicate +// whether to include stacktraces on error wrap / creation. +const IncludesStacktrace = true + +type trace []runtime.Frame + +// set will set the actual trace value +// only when correct build flag is set. +func (t *trace) set(v []runtime.Frame) { + *t = trace(v) +} + +// value returns the actual trace value +// only when correct build flag is set. +func (t trace) value() Callers { + return Callers(t) +} diff --git a/vendor/codeberg.org/gruf/go-errors/v2/callers.go b/vendor/codeberg.org/gruf/go-errors/v2/callers.go deleted file mode 100644 index 3fe84b0c5..000000000 --- a/vendor/codeberg.org/gruf/go-errors/v2/callers.go +++ /dev/null @@ -1,98 +0,0 @@ -package errors - -import ( - "encoding/json" - "runtime" - "strconv" - "strings" - "unsafe" -) - -// Callers is a stacktrace of caller PCs. -type Callers []uintptr - -// GetCallers returns a Callers slice of PCs, of at most 'depth'. -func GetCallers(skip int, depth int) Callers { - rpc := make([]uintptr, depth) - n := runtime.Callers(skip+1, rpc) - return Callers(rpc[0:n]) -} - -// Frames fetches runtime frames for a slice of caller PCs. -func (f Callers) Frames() []runtime.Frame { - // Allocate expected frames slice - frames := make([]runtime.Frame, 0, len(f)) - - // Get frames iterator for PCs - iter := runtime.CallersFrames(f) - - for { - // Get next frame in iter - frame, ok := iter.Next() - if !ok { - break - } - - // Append to frames slice - frames = append(frames, frame) - } - - return frames -} - -// MarshalJSON implements json.Marshaler to provide an easy, simple default. -func (f Callers) MarshalJSON() ([]byte, error) { - // JSON-able frame type - type jsonFrame struct { - Func string `json:"func"` - File string `json:"file"` - Line int `json:"line"` - } - - // Convert to frames - frames := f.Frames() - - // Allocate expected size jsonFrame slice - jsonFrames := make([]jsonFrame, 0, len(f)) - - for i := 0; i < len(frames); i++ { - frame := frames[i] - - // Convert each to jsonFrame object - jsonFrames = append(jsonFrames, jsonFrame{ - Func: funcname(frame.Function), - File: frame.File, - Line: frame.Line, - }) - } - - // marshal converted frames - return json.Marshal(frames) -} - -// String will return a simple string representation of receiving Callers slice. -func (f Callers) String() string { - // Guess-timate to reduce allocs - buf := make([]byte, 0, 64*len(f)) - - // Convert to frames - frames := f.Frames() - - for i := 0; i < len(frames); i++ { - frame := frames[i] - - // Append formatted caller info - fn := funcname(frame.Function) - buf = append(buf, fn+"()\n\t"+frame.File+":"...) - buf = strconv.AppendInt(buf, int64(frame.Line), 10) - buf = append(buf, '\n') - } - - return *(*string)(unsafe.Pointer(&buf)) -} - -// funcname splits a function name with pkg from its path prefix. -func funcname(name string) string { - i := strings.LastIndex(name, "/") - return name[i+1:] -} diff --git a/vendor/codeberg.org/gruf/go-errors/v2/error.go b/vendor/codeberg.org/gruf/go-errors/v2/error.go deleted file mode 100644 index ed1217a29..000000000 --- a/vendor/codeberg.org/gruf/go-errors/v2/error.go +++ /dev/null @@ -1,35 +0,0 @@ -//go:build !notrace -// +build !notrace - -package errors - -type errormsg struct { - msg string - wrap error - stack Callers -} - -func create(msg string, wrap error) *errormsg { - return &errormsg{ - msg: msg, - wrap: wrap, - stack: GetCallers(2, 10), - } -} - -func (err *errormsg) Error() string { - return err.msg -} - -func (err *errormsg) Is(target error) bool { - other, ok := target.(*errormsg) - return ok && (err.msg == other.msg) -} - -func (err *errormsg) Unwrap() error { - return err.wrap -} - -func (err *errormsg) Stacktrace() Callers { - return err.stack -} diff --git a/vendor/codeberg.org/gruf/go-errors/v2/error_notrace.go b/vendor/codeberg.org/gruf/go-errors/v2/error_notrace.go deleted file mode 100644 index e5faf80a2..000000000 --- a/vendor/codeberg.org/gruf/go-errors/v2/error_notrace.go +++ /dev/null @@ -1,33 +0,0 @@ -//go:build notrace -// +build notrace - -package errors - -type errormsg struct { - msg string - wrap error -} - -func create(msg string, wrap error) *errormsg { - return &errormsg{ - msg: msg, - wrap: wrap, - } -} - -func (err *errormsg) Error() string { - return err.msg -} - -func (err *errormsg) Is(target error) bool { - other, ok := target.(*errormsg) - return ok && (err.msg == other.msg) -} - -func (err *errormsg) Unwrap() error { - return err.wrap -} - -func (err *errormsg) Stacktrace() Callers { - return nil -} diff --git a/vendor/codeberg.org/gruf/go-errors/v2/errors.go b/vendor/codeberg.org/gruf/go-errors/v2/errors.go index 2c4689151..d5f1a7ab1 100644 --- a/vendor/codeberg.org/gruf/go-errors/v2/errors.go +++ b/vendor/codeberg.org/gruf/go-errors/v2/errors.go @@ -1,37 +1,248 @@ package errors import ( - "errors" "fmt" + "runtime" ) // New returns a new error created from message. +// +// Note this function cannot be inlined, to ensure expected +// and consistent behaviour in setting trace / caller info. +// +//go:noinline func New(msg string) error { - return create(msg, nil) + var c caller + var t trace + if IncludesCaller { + pcs := make([]uintptr, 1) + _ = runtime.Callers(2, pcs) + fn := runtime.FuncForPC(pcs[0]) + c.set(funcName(fn)) + } + if IncludesStacktrace { + pcs := make([]uintptr, 10) + n := runtime.Callers(2, pcs) + iter := runtime.CallersFrames(pcs[:n]) + t.set(gatherFrames(iter, n)) + } + return &_errormsg{ + cfn: c, + msg: msg, + trc: t, + } } // Newf returns a new error created from message format and args. +// +// Note this function cannot be inlined, to ensure expected +// and consistent behaviour in setting trace / caller info. +// +//go:noinline func Newf(msgf string, args ...interface{}) error { - return create(fmt.Sprintf(msgf, args...), nil) + var c caller + var t trace + if IncludesCaller { + pcs := make([]uintptr, 1) + _ = runtime.Callers(2, pcs) + fn := runtime.FuncForPC(pcs[0]) + c.set(funcName(fn)) + } + if IncludesStacktrace { + pcs := make([]uintptr, 10) + n := runtime.Callers(2, pcs) + iter := runtime.CallersFrames(pcs[:n]) + t.set(gatherFrames(iter, n)) + } + return &_errormsg{ + cfn: c, + msg: fmt.Sprintf(msgf, args...), + trc: t, + } +} + +// NewAt returns a new error created, skipping 'skip' +// frames for trace / caller information, from message. +// +// Note this function cannot be inlined, to ensure expected +// and consistent behaviour in setting trace / caller info. +// +//go:noinline +func NewAt(skip int, msg string) error { + var c caller + var t trace + if IncludesCaller { + pcs := make([]uintptr, 1) + _ = runtime.Callers(skip+1, pcs) + fn := runtime.FuncForPC(pcs[0]) + c.set(funcName(fn)) + } + if IncludesStacktrace { + pcs := make([]uintptr, 10) + n := runtime.Callers(skip+1, pcs) + iter := runtime.CallersFrames(pcs[:n]) + t.set(gatherFrames(iter, n)) + } + return &_errormsg{ + cfn: c, + msg: msg, + trc: t, + } } // Wrap will wrap supplied error within a new error created from message. +// +// Note this function cannot be inlined, to ensure expected +// and consistent behaviour in setting trace / caller info. +// +//go:noinline func Wrap(err error, msg string) error { - return create(msg, err) + if err == nil { + panic("cannot wrap nil error") + } + var c caller + var t trace + if IncludesCaller { + pcs := make([]uintptr, 1) + _ = runtime.Callers(2, pcs) + fn := runtime.FuncForPC(pcs[0]) + c.set(funcName(fn)) + } + if IncludesStacktrace { + pcs := make([]uintptr, 10) + n := runtime.Callers(2, pcs) + iter := runtime.CallersFrames(pcs[:n]) + t.set(gatherFrames(iter, n)) + } + return &_errorwrap{ + cfn: c, + msg: msg, + err: err, + trc: t, + } } // Wrapf will wrap supplied error within a new error created from message format and args. +// +// Note this function cannot be inlined, to ensure expected +// and consistent behaviour in setting trace / caller info. +// +//go:noinline func Wrapf(err error, msgf string, args ...interface{}) error { - return create(fmt.Sprintf(msgf, args...), err) + if err == nil { + panic("cannot wrap nil error") + } + var c caller + var t trace + if IncludesCaller { + pcs := make([]uintptr, 1) + _ = runtime.Callers(2, pcs) + fn := runtime.FuncForPC(pcs[0]) + c.set(funcName(fn)) + } + if IncludesStacktrace { + pcs := make([]uintptr, 10) + n := runtime.Callers(2, pcs) + iter := runtime.CallersFrames(pcs[:n]) + t.set(gatherFrames(iter, n)) + } + return &_errorwrap{ + cfn: c, + msg: fmt.Sprintf(msgf, args...), + err: err, + trc: t, + } +} + +// WrapAt wraps error within new error created from message, +// skipping 'skip' frames for trace / caller information. +// +// Note this function cannot be inlined, to ensure expected +// and consistent behaviour in setting trace / caller info. +// +//go:noinline +func WrapAt(skip int, err error, msg string) error { + if err == nil { + panic("cannot wrap nil error") + } + var c caller + var t trace + if IncludesCaller { + pcs := make([]uintptr, 1) + _ = runtime.Callers(skip+1, pcs) + fn := runtime.FuncForPC(pcs[0]) + c.set(funcName(fn)) + } + if IncludesStacktrace { + pcs := make([]uintptr, 10) + n := runtime.Callers(skip+1, pcs) + iter := runtime.CallersFrames(pcs[:n]) + t.set(gatherFrames(iter, n)) + } + return &_errorwrap{ + cfn: c, + msg: msg, + err: err, + trc: t, + } } // Stacktrace fetches first stored stacktrace of callers from error chain. func Stacktrace(err error) Callers { - var e interface { - Stacktrace() Callers - } - if !errors.As(err, &e) { + if !IncludesStacktrace { + // compile-time check return nil } - return e.Stacktrace() + if e := AsV2[*_errormsg](err); err != nil { + return e.trc.value() + } + if e := AsV2[*_errorwrap](err); err != nil { + return e.trc.value() + } + return nil +} + +type _errormsg struct { + cfn caller + msg string + trc trace +} + +func (err *_errormsg) Error() string { + if IncludesCaller { + fn := err.cfn.value() + return fn + " " + err.msg + } else { + return err.msg + } +} + +func (err *_errormsg) Is(other error) bool { + oerr, ok := other.(*_errormsg) + return ok && oerr.msg == err.msg +} + +type _errorwrap struct { + cfn caller + msg string + err error // wrapped + trc trace +} + +func (err *_errorwrap) Error() string { + if IncludesCaller { + fn := err.cfn.value() + return fn + " " + err.msg + ": " + err.err.Error() + } else { + return err.msg + ": " + err.err.Error() + } +} + +func (err *_errorwrap) Is(other error) bool { + oerr, ok := other.(*_errorwrap) + return ok && oerr.msg == err.msg +} + +func (err *_errorwrap) Unwrap() error { + return err.err } diff --git a/vendor/codeberg.org/gruf/go-errors/v2/once.go b/vendor/codeberg.org/gruf/go-errors/v2/once.go index 83a45a61f..467fe726d 100644 --- a/vendor/codeberg.org/gruf/go-errors/v2/once.go +++ b/vendor/codeberg.org/gruf/go-errors/v2/once.go @@ -2,46 +2,30 @@ package errors import ( "sync/atomic" - "unsafe" ) // OnceError is an error structure that supports safe multi // threaded usage and setting only once (until reset). -type OnceError struct{ err unsafe.Pointer } - -// NewOnce returns a new OnceError instance. -func NewOnce() OnceError { - return OnceError{ - err: nil, - } -} +type OnceError struct{ ptr atomic.Pointer[error] } // Store will safely set the OnceError to value, no-op if nil. -func (e *OnceError) Store(err error) { - // Nothing to do +func (e *OnceError) Store(err error) bool { if err == nil { - return + return false } - - // Only set if not already - atomic.CompareAndSwapPointer( - &e.err, - nil, - unsafe.Pointer(&err), - ) + return e.ptr.CompareAndSwap(nil, &err) } // Load will load the currently stored error. func (e *OnceError) Load() error { - return *(*error)(atomic.LoadPointer(&e.err)) + if ptr := e.ptr.Load(); ptr != nil { + return *ptr + } + return nil } // IsSet returns whether OnceError has been set. -func (e *OnceError) IsSet() bool { - return (atomic.LoadPointer(&e.err) != nil) -} +func (e *OnceError) IsSet() bool { return (e.ptr.Load() != nil) } // Reset will reset the OnceError value. -func (e *OnceError) Reset() { - atomic.StorePointer(&e.err, nil) -} +func (e *OnceError) Reset() { e.ptr.Store(nil) } diff --git a/vendor/codeberg.org/gruf/go-errors/v2/runtime.go b/vendor/codeberg.org/gruf/go-errors/v2/runtime.go new file mode 100644 index 000000000..0c8cf11cd --- /dev/null +++ b/vendor/codeberg.org/gruf/go-errors/v2/runtime.go @@ -0,0 +1,97 @@ +package errors + +import ( + "encoding/json" + "runtime" + "strconv" + "strings" + "unsafe" +) + +// Callers ... +type Callers []runtime.Frame + +// MarshalJSON implements json.Marshaler to provide an easy, simple default. +func (c Callers) MarshalJSON() ([]byte, error) { + // JSON-able frame type + type jsonFrame struct { + Func string `json:"func"` + File string `json:"file"` + Line int `json:"line"` + } + + // Allocate expected size jsonFrame slice + jsonFrames := make([]jsonFrame, len(c)) + + // Convert each to jsonFrame object + for i := 0; i < len(c); i++ { + frame := c[i] + jsonFrames[i] = jsonFrame{ + Func: funcName(frame.Func), + File: frame.File, + Line: frame.Line, + } + } + + // marshal converted frames + return json.Marshal(jsonFrames) +} + +// String will return a simple string representation of receiving Callers slice. +func (c Callers) String() string { + // Guess-timate to reduce allocs + buf := make([]byte, 0, 64*len(c)) + + for i := 0; i < len(c); i++ { + frame := c[i] + + // Append formatted caller info + fn := funcName(frame.Func) + buf = append(buf, fn+"()\n\t"+frame.File+":"...) + buf = strconv.AppendInt(buf, int64(frame.Line), 10) + buf = append(buf, '\n') + } + + return *(*string)(unsafe.Pointer(&buf)) +} + +// funcName formats a function name to a quickly-readable string. +func funcName(fn *runtime.Func) string { + if fn == nil { + return "" + } + + // Get func name + // for formatting. + name := fn.Name() + + // Drop all but the package name and function name, no mod path + if idx := strings.LastIndex(name, "/"); idx >= 0 { + name = name[idx+1:] + } + + const params = `[...]` + + // Drop any generic type parameter markers + if idx := strings.Index(name, params); idx >= 0 { + name = name[:idx] + name[idx+len(params):] + } + + return name +} + +// gatherFrames collates runtime frames from a frame iterator. +func gatherFrames(iter *runtime.Frames, n int) Callers { + if iter == nil { + return nil + } + frames := make([]runtime.Frame, 0, n) + for { + f, ok := iter.Next() + if !ok { + break + } + frames = append(frames, f) + } + return frames +} diff --git a/vendor/codeberg.org/gruf/go-errors/v2/standard.go b/vendor/codeberg.org/gruf/go-errors/v2/standard.go index 1d2c71c5f..3739416dc 100644 --- a/vendor/codeberg.org/gruf/go-errors/v2/standard.go +++ b/vendor/codeberg.org/gruf/go-errors/v2/standard.go @@ -1,133 +1,37 @@ package errors import ( - "errors" - "reflect" _ "unsafe" - - "codeberg.org/gruf/go-bitutil" ) -// errtype is a ptr to the error interface type. -var errtype = reflect.TypeOf((*error)(nil)).Elem() - -// Comparable is functionally equivalent to calling errors.Is() on multiple errors (up to a max of 64). -func Comparable(err error, targets ...error) bool { - var flags bitutil.Flags64 - - // Flags only has 64 bit-slots - if len(targets) > 64 { - panic("too many targets") - } - - for i := 0; i < len(targets); { - if targets[i] == nil { - if err == nil { - return true - } - - // Drop nil targets from slice. - copy(targets[i:], targets[i+1:]) - targets = targets[:len(targets)-1] - continue - } - - // Check if this error is directly comparable - if reflect.TypeOf(targets[i]).Comparable() { - flags = flags.Set(uint8(i)) - } - - i++ - } - - for err != nil { - // Check if this layer supports .Is interface - is, ok := err.(interface{ Is(error) bool }) - - if !ok { - // Error does not support interface - // - // Only try perform direct compare - for i := 0; i < len(targets); i++ { - // Try directly compare errors - if flags.Get(uint8(i)) && - err == targets[i] { - return true - } - } - } else { - // Error supports the .Is interface - // - // Perform direct compare AND .Is() - for i := 0; i < len(targets); i++ { - if (flags.Get(uint8(i)) && - err == targets[i]) || - is.Is(targets[i]) { - return true - } - } - } - - // Unwrap to next layer - err = errors.Unwrap(err) - } - - return false -} - -// Assignable is functionally equivalent to calling errors.As() on multiple errors, -// except that it only checks assignability as opposed to setting the target. -func Assignable(err error, targets ...error) bool { - if err == nil { - // Fastest case. - return false - } - - for i := 0; i < len(targets); { - if targets[i] == nil { - // Drop nil targets from slice. - copy(targets[i:], targets[i+1:]) - targets = targets[:len(targets)-1] - continue - } - i++ - } - - for err != nil { - // Check if this layer supports .As interface - as, ok := err.(interface{ As(any) bool }) - - // Get reflected err type. - te := reflect.TypeOf(err) - - if !ok { - // Error does not support interface. - // - // Check assignability using reflection. - for i := 0; i < len(targets); i++ { - tt := reflect.TypeOf(targets[i]) - if te.AssignableTo(tt) { - return true - } - } - } else { - // Error supports the .As interface. - // - // Check using .As() and reflection. - for i := 0; i < len(targets); i++ { - if as.As(targets[i]) { - return true - } else if tt := reflect.TypeOf(targets[i]); // nocollapse - te.AssignableTo(tt) { - return true - } - } +// Is reports whether any error in err's tree matches target. +// +// The tree consists of err itself, followed by the errors obtained by repeatedly +// calling Unwrap. When err wraps multiple errors, Is examines err followed by a +// depth-first traversal of its children. +// +// An error is considered to match a target if it is equal to that target or if +// it implements a method Is(error) bool such that Is(target) returns true. +// +// An error type might provide an Is method so it can be treated as equivalent +// to an existing error. For example, if MyError defines +// +// func (m MyError) Is(target error) bool { return target == fs.ErrExist } +// +// then Is(MyError{}, fs.ErrExist) returns true. See [syscall.Errno.Is] for +// an example in the standard library. An Is method should only shallowly +// compare err and the target and not call Unwrap on either. +// +//go:linkname Is errors.Is +func Is(err error, target error) bool + +// IsV2 calls Is(err, target) for each target within targets. +func IsV2(err error, targets ...error) bool { + for _, target := range targets { + if Is(err, target) { + return true } - - // Unwrap to next layer. - err = errors.Unwrap(err) } - return false } @@ -152,8 +56,79 @@ func Assignable(err error, targets ...error) bool { //go:linkname As errors.As func As(err error, target any) bool +// AsV2 is functionally similar to As(), instead +// leveraging generics to handle allocation and +// returning of a concrete generic parameter type. +func AsV2[Type any](err error) Type { + var t Type + var ok bool + errs := []error{err} + for len(errs) > 0 { + // Pop next error to check. + err := errs[len(errs)-1] + errs = errs[:len(errs)-1] + + // Check direct type. + t, ok = err.(Type) + if ok { + return t + } + + // Look for .As() support. + as, ok := err.(interface { + As(target any) bool + }) + + if ok { + // Attempt .As(). + if as.As(&t) { + return t + } + } + + // Try unwrap errors. + switch u := err.(type) { + case interface{ Unwrap() error }: + errs = append(errs, u.Unwrap()) + case interface{ Unwrap() []error }: + errs = append(errs, u.Unwrap()...) + } + } + return t +} + // Unwrap returns the result of calling the Unwrap method on err, if err's -// type contains an Unwrap method returning error. Otherwise, Unwrap returns nil. +// type contains an Unwrap method returning error. +// Otherwise, Unwrap returns nil. +// +// Unwrap only calls a method of the form "Unwrap() error". +// In particular Unwrap does not unwrap errors returned by [Join]. // //go:linkname Unwrap errors.Unwrap func Unwrap(err error) error + +// UnwrapV2 is functionally similar to Unwrap(), except that +// it also handles the case of interface{ Unwrap() []error }. +func UnwrapV2(err error) []error { + switch u := err.(type) { + case interface{ Unwrap() error }: + if e := u.Unwrap(); err != nil { + return []error{e} + } + case interface{ Unwrap() []error }: + return u.Unwrap() + } + return nil +} + +// Join returns an error that wraps the given errors. +// Any nil error values are discarded. +// Join returns nil if every value in errs is nil. +// The error formats as the concatenation of the strings obtained +// by calling the Error method of each element of errs, with a newline +// between each string. +// +// A non-nil error returned by Join implements the Unwrap() []error method. +// +//go:linkname Join errors.Join +func Join(errs ...error) error diff --git a/vendor/codeberg.org/gruf/go-errors/v2/value.go b/vendor/codeberg.org/gruf/go-errors/v2/value.go index 6d7ec3a25..876e5f80e 100644 --- a/vendor/codeberg.org/gruf/go-errors/v2/value.go +++ b/vendor/codeberg.org/gruf/go-errors/v2/value.go @@ -1,56 +1,50 @@ package errors -import "errors" - // WithValue wraps err to store given key-value pair, accessible via Value() function. func WithValue(err error, key any, value any) error { if err == nil { panic("nil error") } - return &errWithValue{ + var kvs []kv + if e := AsV2[*errWithValues](err); e != nil { + kvs = e.kvs + } + return &errWithValues{ err: err, - key: key, - val: value, + kvs: append(kvs, kv{key, value}), } } // Value searches for value stored under given key in error chain. func Value(err error, key any) any { - var e *errWithValue - - if !errors.As(err, &e) { - return nil + if e := AsV2[*errWithValues](err); e != nil { + return e.Value(key) } - - return e.Value(key) + return nil } -type errWithValue struct { +// simple key-value type. +type kv struct{ k, v any } + +// errWithValues wraps an error to provide key-value storage. +type errWithValues struct { err error - key any - val any + kvs []kv } -func (e *errWithValue) Error() string { +func (e *errWithValues) Error() string { return e.err.Error() } -func (e *errWithValue) Is(target error) bool { - return e.err == target +func (e *errWithValues) Unwrap() error { + return e.err } -func (e *errWithValue) Unwrap() error { - return Unwrap(e.err) -} - -func (e *errWithValue) Value(key any) any { - for { - if key == e.key { - return e.val - } - - if !errors.As(e.err, &e) { - return nil +func (e *errWithValues) Value(key any) any { + for i := range e.kvs { + if e.kvs[i].k == key { + return e.kvs[i].v } } + return nil } |