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

184 lines
6.1 KiB
Go
Raw Normal View History

package merry
import "fmt"
// Wrapper knows how to wrap errors with context information.
type Wrapper interface {
// Wrap returns a new error, wrapping the argument, and typically adding some context information.
// skipCallers is how many callers to skip when capturing a stack to skip to the caller of the merry
// API surface. It's intended to make it possible to write wrappers which capture stacktraces. e.g.
//
// func CaptureStack() Wrapper {
// return WrapperFunc(func(err error, skipCallers int) error {
// s := make([]uintptr, 50)
// // Callers
// l := runtime.Callers(2+skipCallers, s[:])
// return WithStack(s[:l]).Wrap(err, skipCallers + 1)
// })
// }
Wrap(err error, skipCallers int) error
}
// WrapperFunc implements Wrapper.
type WrapperFunc func(error, int) error
// Wrap implements the Wrapper interface.
func (w WrapperFunc) Wrap(err error, callerDepth int) error {
return w(err, callerDepth+1)
}
// WithValue associates a key/value pair with an error.
func WithValue(key, value interface{}) Wrapper {
return WrapperFunc(func(err error, _ int) error {
return Set(err, key, value)
})
}
// WithMessage overrides the value returned by err.Error().
func WithMessage(msg string) Wrapper {
return WithValue(errKeyMessage, msg)
}
// WithMessagef overrides the value returned by err.Error().
func WithMessagef(format string, args ...interface{}) Wrapper {
return WrapperFunc(func(err error, _ int) error {
if err == nil {
return nil
}
return Set(err, errKeyMessage, fmt.Sprintf(format, args...))
})
}
// WithUserMessage associates an end-user message with an error.
func WithUserMessage(msg string) Wrapper {
return WithValue(errKeyUserMessage, msg)
}
// WithUserMessagef associates a formatted end-user message with an error.
func WithUserMessagef(format string, args ...interface{}) Wrapper {
return WrapperFunc(func(err error, _ int) error {
if err == nil {
return nil
}
return Set(err, errKeyUserMessage, fmt.Sprintf(format, args...))
})
}
// AppendMessage a message after the current error message, in the format "original: new".
func AppendMessage(msg string) Wrapper {
return WrapperFunc(func(err error, _ int) error {
if err == nil {
return nil
}
return Set(err, errKeyMessage, err.Error()+": "+msg)
})
}
// AppendMessagef is the same as AppendMessage, but with a formatted message.
func AppendMessagef(format string, args ...interface{}) Wrapper {
return WrapperFunc(func(err error, _ int) error {
if err == nil {
return nil
}
return Set(err, errKeyMessage, err.Error()+": "+fmt.Sprintf(format, args...))
})
}
// PrependMessage a message before the current error message, in the format "new: original".
func PrependMessage(msg string) Wrapper {
return WrapperFunc(func(err error, _ int) error {
if err == nil {
return nil
}
return Set(err, errKeyMessage, msg+": "+err.Error())
})
}
// PrependMessagef is the same as PrependMessage, but with a formatted message.
func PrependMessagef(format string, args ...interface{}) Wrapper {
return WrapperFunc(func(err error, _ int) error {
if err == nil {
return nil
}
return Set(err, errKeyMessage, fmt.Sprintf(format, args...)+": "+err.Error())
})
}
// WithHTTPCode associates an HTTP status code with an error.
func WithHTTPCode(statusCode int) Wrapper {
return WithValue(errKeyHTTPCode, statusCode)
}
// WithStack associates a stack of caller frames with an error. Generally, this package
// will automatically capture and associate a stack with errors which are created or
// wrapped by this package. But this allows the caller to associate an externally
// generated stack.
func WithStack(stack []uintptr) Wrapper {
return WithValue(errKeyStack, stack)
}
// WithFormattedStack associates a stack of pre-formatted strings describing frames of a
// stacktrace. Generally, a formatted stack is generated from the raw []uintptr stack
// associated with the error, but a pre-formatted stack can be associated with the error
// instead, and takes precedence over the raw stack. This is useful if pre-formatted
// stack information is coming from some other source.
func WithFormattedStack(stack []string) Wrapper {
return WithValue(errKeyStack, stack)
}
// NoCaptureStack will suppress capturing a stack, even if StackCaptureEnabled() == true.
func NoCaptureStack() Wrapper {
return WrapperFunc(func(err error, _ int) error {
// if this err already has a stack set, there is no need to set the
// stack property again, and we don't want to override the prior the stack
if HasStack(err) {
return err
}
return Set(err, errKeyStack, nil)
})
}
// CaptureStack will override an earlier stack with a stack captured from the current
// call site. If StackCaptureEnabled() == false, this is a no-op.
//
// If force is set, StackCaptureEnabled() will be ignored: a stack will always be captured.
func CaptureStack(force bool) Wrapper {
return WrapperFunc(func(err error, callerDepth int) error {
return captureStack(err, callerDepth+1, force || StackCaptureEnabled())
})
}
// WithCause sets one error as the cause of another error. This is useful for associating errors
// from lower API levels with sentinel errors in higher API levels. errors.Is() and errors.As()
// will traverse both the main chain of error wrappers, as well as down the chain of causes.
func WithCause(err error) Wrapper {
return WrapperFunc(func(nerr error, _ int) error {
if nerr == nil {
return nil
}
return &errWithCause{err: nerr, cause: err}
})
}
// Set wraps an error with a key/value pair. This is the simplest form of associating
// a value with an error. It does not capture a stacktrace, invoke hooks, or do any
// other processing. It is mainly intended as a primitive for writing Wrapper implementations.
//
// if err is nil, returns nil.
//
// Keeping this private for now. If it proves useful, it may be made public later, but
// for now, external packages can get the same behavor with this:
//
// WithValue(key, value).Wrap(err)
//
func Set(err error, key, value interface{}) error {
if err == nil {
return nil
}
return &errWithValue{
err: err,
key: key,
value: value,
}
}