|
@@ -5,6 +5,8 @@ import (
|
|
"reflect"
|
|
"reflect"
|
|
)
|
|
)
|
|
|
|
|
|
|
|
+const resumableDefault = false
|
|
|
|
+
|
|
var (
|
|
var (
|
|
// ErrShortBytes is returned when the
|
|
// ErrShortBytes is returned when the
|
|
// slice being decoded is too short to
|
|
// slice being decoded is too short to
|
|
@@ -26,84 +28,240 @@ type Error interface {
|
|
// Resumable returns whether
|
|
// Resumable returns whether
|
|
// or not the error means that
|
|
// or not the error means that
|
|
// the stream of data is malformed
|
|
// the stream of data is malformed
|
|
- // and the information is unrecoverable.
|
|
|
|
|
|
+ // and the information is unrecoverable.
|
|
Resumable() bool
|
|
Resumable() bool
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// contextError allows msgp Error instances to be enhanced with additional
|
|
|
|
+// context about their origin.
|
|
|
|
+type contextError interface {
|
|
|
|
+ Error
|
|
|
|
+
|
|
|
|
+ // withContext must not modify the error instance - it must clone and
|
|
|
|
+ // return a new error with the context added.
|
|
|
|
+ withContext(ctx string) error
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Cause returns the underlying cause of an error that has been wrapped
|
|
|
|
+// with additional context.
|
|
|
|
+func Cause(e error) error {
|
|
|
|
+ out := e
|
|
|
|
+ if e, ok := e.(errWrapped); ok && e.cause != nil {
|
|
|
|
+ out = e.cause
|
|
|
|
+ }
|
|
|
|
+ return out
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Resumable returns whether or not the error means that the stream of data is
|
|
|
|
+// malformed and the information is unrecoverable.
|
|
|
|
+func Resumable(e error) bool {
|
|
|
|
+ if e, ok := e.(Error); ok {
|
|
|
|
+ return e.Resumable()
|
|
|
|
+ }
|
|
|
|
+ return resumableDefault
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// WrapError wraps an error with additional context that allows the part of the
|
|
|
|
+// serialized type that caused the problem to be identified. Underlying errors
|
|
|
|
+// can be retrieved using Cause()
|
|
|
|
+//
|
|
|
|
+// The input error is not modified - a new error should be returned.
|
|
|
|
+//
|
|
|
|
+// ErrShortBytes is not wrapped with any context due to backward compatibility
|
|
|
|
+// issues with the public API.
|
|
|
|
+//
|
|
|
|
+func WrapError(err error, ctx ...interface{}) error {
|
|
|
|
+ switch e := err.(type) {
|
|
|
|
+ case errShort:
|
|
|
|
+ return e
|
|
|
|
+ case contextError:
|
|
|
|
+ return e.withContext(ctxString(ctx))
|
|
|
|
+ default:
|
|
|
|
+ return errWrapped{cause: err, ctx: ctxString(ctx)}
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// ctxString converts the incoming interface{} slice into a single string.
|
|
|
|
+func ctxString(ctx []interface{}) string {
|
|
|
|
+ out := ""
|
|
|
|
+ for idx, cv := range ctx {
|
|
|
|
+ if idx > 0 {
|
|
|
|
+ out += "/"
|
|
|
|
+ }
|
|
|
|
+ out += fmt.Sprintf("%v", cv)
|
|
|
|
+ }
|
|
|
|
+ return out
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func addCtx(ctx, add string) string {
|
|
|
|
+ if ctx != "" {
|
|
|
|
+ return add + "/" + ctx
|
|
|
|
+ } else {
|
|
|
|
+ return add
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// errWrapped allows arbitrary errors passed to WrapError to be enhanced with
|
|
|
|
+// context and unwrapped with Cause()
|
|
|
|
+type errWrapped struct {
|
|
|
|
+ cause error
|
|
|
|
+ ctx string
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (e errWrapped) Error() string {
|
|
|
|
+ if e.ctx != "" {
|
|
|
|
+ return fmt.Sprintf("%s at %s", e.cause, e.ctx)
|
|
|
|
+ } else {
|
|
|
|
+ return e.cause.Error()
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (e errWrapped) Resumable() bool {
|
|
|
|
+ if e, ok := e.cause.(Error); ok {
|
|
|
|
+ return e.Resumable()
|
|
|
|
+ }
|
|
|
|
+ return resumableDefault
|
|
|
|
+}
|
|
|
|
+
|
|
type errShort struct{}
|
|
type errShort struct{}
|
|
|
|
|
|
func (e errShort) Error() string { return "msgp: too few bytes left to read object" }
|
|
func (e errShort) Error() string { return "msgp: too few bytes left to read object" }
|
|
func (e errShort) Resumable() bool { return false }
|
|
func (e errShort) Resumable() bool { return false }
|
|
|
|
|
|
-type errFatal struct{}
|
|
|
|
|
|
+type errFatal struct {
|
|
|
|
+ ctx string
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (f errFatal) Error() string {
|
|
|
|
+ out := "msgp: fatal decoding error (unreachable code)"
|
|
|
|
+ if f.ctx != "" {
|
|
|
|
+ out += " at " + f.ctx
|
|
|
|
+ }
|
|
|
|
+ return out
|
|
|
|
+}
|
|
|
|
|
|
-func (f errFatal) Error() string { return "msgp: fatal decoding error (unreachable code)" }
|
|
|
|
func (f errFatal) Resumable() bool { return false }
|
|
func (f errFatal) Resumable() bool { return false }
|
|
|
|
|
|
|
|
+func (f errFatal) withContext(ctx string) error { f.ctx = addCtx(f.ctx, ctx); return f }
|
|
|
|
+
|
|
// ArrayError is an error returned
|
|
// ArrayError is an error returned
|
|
// when decoding a fix-sized array
|
|
// when decoding a fix-sized array
|
|
// of the wrong size
|
|
// of the wrong size
|
|
type ArrayError struct {
|
|
type ArrayError struct {
|
|
Wanted uint32
|
|
Wanted uint32
|
|
Got uint32
|
|
Got uint32
|
|
|
|
+ ctx string
|
|
}
|
|
}
|
|
|
|
|
|
// Error implements the error interface
|
|
// Error implements the error interface
|
|
func (a ArrayError) Error() string {
|
|
func (a ArrayError) Error() string {
|
|
- return fmt.Sprintf("msgp: wanted array of size %d; got %d", a.Wanted, a.Got)
|
|
|
|
|
|
+ out := fmt.Sprintf("msgp: wanted array of size %d; got %d", a.Wanted, a.Got)
|
|
|
|
+ if a.ctx != "" {
|
|
|
|
+ out += " at " + a.ctx
|
|
|
|
+ }
|
|
|
|
+ return out
|
|
}
|
|
}
|
|
|
|
|
|
// Resumable is always 'true' for ArrayErrors
|
|
// Resumable is always 'true' for ArrayErrors
|
|
func (a ArrayError) Resumable() bool { return true }
|
|
func (a ArrayError) Resumable() bool { return true }
|
|
|
|
|
|
|
|
+func (a ArrayError) withContext(ctx string) error { a.ctx = addCtx(a.ctx, ctx); return a }
|
|
|
|
+
|
|
// IntOverflow is returned when a call
|
|
// IntOverflow is returned when a call
|
|
// would downcast an integer to a type
|
|
// would downcast an integer to a type
|
|
// with too few bits to hold its value.
|
|
// with too few bits to hold its value.
|
|
type IntOverflow struct {
|
|
type IntOverflow struct {
|
|
Value int64 // the value of the integer
|
|
Value int64 // the value of the integer
|
|
FailedBitsize int // the bit size that the int64 could not fit into
|
|
FailedBitsize int // the bit size that the int64 could not fit into
|
|
|
|
+ ctx string
|
|
}
|
|
}
|
|
|
|
|
|
// Error implements the error interface
|
|
// Error implements the error interface
|
|
func (i IntOverflow) Error() string {
|
|
func (i IntOverflow) Error() string {
|
|
- return fmt.Sprintf("msgp: %d overflows int%d", i.Value, i.FailedBitsize)
|
|
|
|
|
|
+ str := fmt.Sprintf("msgp: %d overflows int%d", i.Value, i.FailedBitsize)
|
|
|
|
+ if i.ctx != "" {
|
|
|
|
+ str += " at " + i.ctx
|
|
|
|
+ }
|
|
|
|
+ return str
|
|
}
|
|
}
|
|
|
|
|
|
// Resumable is always 'true' for overflows
|
|
// Resumable is always 'true' for overflows
|
|
func (i IntOverflow) Resumable() bool { return true }
|
|
func (i IntOverflow) Resumable() bool { return true }
|
|
|
|
|
|
|
|
+func (i IntOverflow) withContext(ctx string) error { i.ctx = addCtx(i.ctx, ctx); return i }
|
|
|
|
+
|
|
// UintOverflow is returned when a call
|
|
// UintOverflow is returned when a call
|
|
// would downcast an unsigned integer to a type
|
|
// would downcast an unsigned integer to a type
|
|
// with too few bits to hold its value
|
|
// with too few bits to hold its value
|
|
type UintOverflow struct {
|
|
type UintOverflow struct {
|
|
Value uint64 // value of the uint
|
|
Value uint64 // value of the uint
|
|
FailedBitsize int // the bit size that couldn't fit the value
|
|
FailedBitsize int // the bit size that couldn't fit the value
|
|
|
|
+ ctx string
|
|
}
|
|
}
|
|
|
|
|
|
// Error implements the error interface
|
|
// Error implements the error interface
|
|
func (u UintOverflow) Error() string {
|
|
func (u UintOverflow) Error() string {
|
|
- return fmt.Sprintf("msgp: %d overflows uint%d", u.Value, u.FailedBitsize)
|
|
|
|
|
|
+ str := fmt.Sprintf("msgp: %d overflows uint%d", u.Value, u.FailedBitsize)
|
|
|
|
+ if u.ctx != "" {
|
|
|
|
+ str += " at " + u.ctx
|
|
|
|
+ }
|
|
|
|
+ return str
|
|
}
|
|
}
|
|
|
|
|
|
// Resumable is always 'true' for overflows
|
|
// Resumable is always 'true' for overflows
|
|
func (u UintOverflow) Resumable() bool { return true }
|
|
func (u UintOverflow) Resumable() bool { return true }
|
|
|
|
|
|
|
|
+func (u UintOverflow) withContext(ctx string) error { u.ctx = addCtx(u.ctx, ctx); return u }
|
|
|
|
+
|
|
|
|
+// UintBelowZero is returned when a call
|
|
|
|
+// would cast a signed integer below zero
|
|
|
|
+// to an unsigned integer.
|
|
|
|
+type UintBelowZero struct {
|
|
|
|
+ Value int64 // value of the incoming int
|
|
|
|
+ ctx string
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Error implements the error interface
|
|
|
|
+func (u UintBelowZero) Error() string {
|
|
|
|
+ str := fmt.Sprintf("msgp: attempted to cast int %d to unsigned", u.Value)
|
|
|
|
+ if u.ctx != "" {
|
|
|
|
+ str += " at " + u.ctx
|
|
|
|
+ }
|
|
|
|
+ return str
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Resumable is always 'true' for overflows
|
|
|
|
+func (u UintBelowZero) Resumable() bool { return true }
|
|
|
|
+
|
|
|
|
+func (u UintBelowZero) withContext(ctx string) error {
|
|
|
|
+ u.ctx = ctx
|
|
|
|
+ return u
|
|
|
|
+}
|
|
|
|
+
|
|
// A TypeError is returned when a particular
|
|
// A TypeError is returned when a particular
|
|
// decoding method is unsuitable for decoding
|
|
// decoding method is unsuitable for decoding
|
|
// a particular MessagePack value.
|
|
// a particular MessagePack value.
|
|
type TypeError struct {
|
|
type TypeError struct {
|
|
Method Type // Type expected by method
|
|
Method Type // Type expected by method
|
|
Encoded Type // Type actually encoded
|
|
Encoded Type // Type actually encoded
|
|
|
|
+
|
|
|
|
+ ctx string
|
|
}
|
|
}
|
|
|
|
|
|
// Error implements the error interface
|
|
// Error implements the error interface
|
|
func (t TypeError) Error() string {
|
|
func (t TypeError) Error() string {
|
|
- return fmt.Sprintf("msgp: attempted to decode type %q with method for %q", t.Encoded, t.Method)
|
|
|
|
|
|
+ out := fmt.Sprintf("msgp: attempted to decode type %q with method for %q", t.Encoded, t.Method)
|
|
|
|
+ if t.ctx != "" {
|
|
|
|
+ out += " at " + t.ctx
|
|
|
|
+ }
|
|
|
|
+ return out
|
|
}
|
|
}
|
|
|
|
|
|
// Resumable returns 'true' for TypeErrors
|
|
// Resumable returns 'true' for TypeErrors
|
|
func (t TypeError) Resumable() bool { return true }
|
|
func (t TypeError) Resumable() bool { return true }
|
|
|
|
|
|
|
|
+func (t TypeError) withContext(ctx string) error { t.ctx = addCtx(t.ctx, ctx); return t }
|
|
|
|
+
|
|
// returns either InvalidPrefixError or
|
|
// returns either InvalidPrefixError or
|
|
// TypeError depending on whether or not
|
|
// TypeError depending on whether or not
|
|
// the prefix is recognized
|
|
// the prefix is recognized
|
|
@@ -133,10 +291,24 @@ func (i InvalidPrefixError) Resumable() bool { return false }
|
|
// to a function that takes `interface{}`.
|
|
// to a function that takes `interface{}`.
|
|
type ErrUnsupportedType struct {
|
|
type ErrUnsupportedType struct {
|
|
T reflect.Type
|
|
T reflect.Type
|
|
|
|
+
|
|
|
|
+ ctx string
|
|
}
|
|
}
|
|
|
|
|
|
// Error implements error
|
|
// Error implements error
|
|
-func (e *ErrUnsupportedType) Error() string { return fmt.Sprintf("msgp: type %q not supported", e.T) }
|
|
|
|
|
|
+func (e *ErrUnsupportedType) Error() string {
|
|
|
|
+ out := fmt.Sprintf("msgp: type %q not supported", e.T)
|
|
|
|
+ if e.ctx != "" {
|
|
|
|
+ out += " at " + e.ctx
|
|
|
|
+ }
|
|
|
|
+ return out
|
|
|
|
+}
|
|
|
|
|
|
// Resumable returns 'true' for ErrUnsupportedType
|
|
// Resumable returns 'true' for ErrUnsupportedType
|
|
func (e *ErrUnsupportedType) Resumable() bool { return true }
|
|
func (e *ErrUnsupportedType) Resumable() bool { return true }
|
|
|
|
+
|
|
|
|
+func (e *ErrUnsupportedType) withContext(ctx string) error {
|
|
|
|
+ o := *e
|
|
|
|
+ o.ctx = addCtx(o.ctx, ctx)
|
|
|
|
+ return &o
|
|
|
|
+}
|