ceph-csi/vendor/github.com/ansel1/merry/v2/impl.go

168 lines
4.3 KiB
Go
Raw Permalink Normal View History

package merry
import (
"errors"
"fmt"
"reflect"
)
type errKey int
const (
errKeyNone errKey = iota
errKeyStack
errKeyMessage
errKeyHTTPCode
errKeyUserMessage
errKeyForceCapture
errKeyHooked
)
func (e errKey) String() string {
switch e {
case errKeyNone:
return "none"
case errKeyStack:
return "stack"
case errKeyMessage:
return "message"
case errKeyHTTPCode:
return "http status code"
case errKeyUserMessage:
return "user message"
case errKeyForceCapture:
return "force stack capture"
default:
return ""
}
}
type errWithValue struct {
err error
key, value interface{}
}
// Format implements fmt.Formatter
func (e *errWithValue) Format(s fmt.State, verb rune) {
Format(s, verb, e)
}
// Error implements golang's error interface
// returns the message value if set, otherwise
// delegates to inner error
func (e *errWithValue) Error() string {
if e.key == errKeyMessage {
if s, ok := e.value.(string); ok {
return s
}
}
return e.err.Error()
}
// String implements fmt.Stringer
func (e *errWithValue) String() string {
return e.Error()
}
// Unwrap returns the next wrapped error.
func (e *errWithValue) Unwrap() error {
return e.err
}
// isMerryError is a marker method for identifying error types implemented by this package.
func (e *errWithValue) isMerryError() {}
type errWithCause struct {
err error
cause error
}
func (e *errWithCause) Unwrap() error {
// skip through any directly nested errWithCauses.
// our implementation of Is/As already recursed through them,
// so we want to dig down to the first non-errWithCause.
nextErr := e.err
for {
if e, ok := nextErr.(*errWithCause); ok {
nextErr = e.err
} else {
break
}
}
// errWithCause.Is/As() also already checked nextErr, so we want to
// unwrap it and get to the next error down.
nextErr = errors.Unwrap(nextErr)
// we've reached the end of this wrapper chain. Return the cause.
if nextErr == nil {
return e.cause
}
// return a new errWithCause wrapper, wrapping next error, but bundling
// it will our cause, ignoring the causes of the errWithCauses we skip
// over above. This is how we carry the latest cause along as we unwrap
// the chain. When we get to the end of the chain, we'll return this latest
// cause.
return &errWithCause{err: nextErr, cause: e.cause}
}
func (e *errWithCause) String() string {
return e.Error()
}
func (e *errWithCause) Error() string {
return e.err.Error()
}
func (e *errWithCause) Format(f fmt.State, verb rune) {
Format(f, verb, e)
}
// errWithCause needs to provide custome implementations of Is and As.
// errors.Is() doesn't work on errWithCause because error.Is() uses errors.Unwrap() to traverse the error
// chain. But errWithCause.Unwrap() doesn't return the next error in the chain. Instead,
// it wraps the next error in a shim. The standard Is/As tests would compare the shim to the target.
// We need to override Is/As to compare the target to the error inside the shim.
func (e *errWithCause) Is(target error) bool {
// This does most of what errors.Is() does, by delegating
// to the nested error. But it does not use Unwrap to recurse
// any further. This just compares target with next error in the stack.
isComparable := reflect.TypeOf(target).Comparable()
if isComparable && e.err == target {
return true
}
// since errWithCause implements Is(), this will effectively recurse through
// any directly nested errWithCauses.
if x, ok := e.err.(interface{ Is(error) bool }); ok && x.Is(target) {
return true
}
return false
}
func (e *errWithCause) As(target interface{}) bool {
// This does most of what errors.As() does, by delegating
// to the nested error. But it does not use Unwrap to recurse
// any further. This just compares target with next error in the stack.
val := reflect.ValueOf(target)
typ := val.Type()
targetType := typ.Elem()
if reflect.TypeOf(e.err).AssignableTo(targetType) {
val.Elem().Set(reflect.ValueOf(e.err))
return true
}
// since errWithCause implements As(), this will effectively recurse through
// any directly nested errWithCauses.
if x, ok := e.err.(interface{ As(interface{}) bool }); ok && x.As(target) {
return true
}
return false
}
// isMerryError is a marker method for identifying error types implemented by this package.
func (e *errWithCause) isMerryError() {}