Browse Source

codec: use types for encode and decode errors, which expose underlying error

Fixes #261
Ugorji Nwoke 7 years ago
parent
commit
a65ca39637
4 changed files with 57 additions and 16 deletions
  1. 2 2
      codec/codec_test.go
  2. 11 2
      codec/decode.go
  3. 10 2
      codec/encode.go
  4. 34 10
      codec/helper.go

+ 2 - 2
codec/codec_test.go

@@ -376,12 +376,12 @@ func testInit() {
 	// and use on some places for testSimpleH e.g. for time.Time and wrapInt64
 	var (
 		myExtEncFn = func(x BytesExt, rv reflect.Value) (bs []byte, err error) {
-			defer panicToErr(errstrDecoratorDef{}, &err)
+			defer panicToErr(errDecoratorDef{}, &err)
 			bs = x.WriteExt(rv.Interface())
 			return
 		}
 		myExtDecFn = func(x BytesExt, rv reflect.Value, bs []byte) (err error) {
-			defer panicToErr(errstrDecoratorDef{}, &err)
+			defer panicToErr(errDecoratorDef{}, &err)
 			x.ReadExt(rv.Interface(), bs)
 			return
 		}

+ 11 - 2
codec/decode.go

@@ -126,6 +126,15 @@ type decDriver interface {
 	uncacheRead()
 }
 
+type decodeError struct {
+	codecError
+	pos int
+}
+
+func (d decodeError) Error() string {
+	return fmt.Sprintf("%s decode error [pos %d]: %v", d.name, d.pos, d.err)
+}
+
 type decDriverNoopContainerReader struct{}
 
 func (x decDriverNoopContainerReader) ReadArrayStart() (v int) { return }
@@ -2433,8 +2442,8 @@ func (d *Decoder) rawBytes() []byte {
 	return bs2
 }
 
-func (d *Decoder) wrapErrstr(v interface{}, err *error) {
-	*err = fmt.Errorf("%s decode error [pos %d]: %v", d.hh.Name(), d.r.numread(), v)
+func (d *Decoder) wrapErr(v interface{}, err *error) {
+	*err = decodeError{codecError: codecError{name: d.hh.Name(), err: v}, pos: d.r.numread()}
 }
 
 // NumBytesRead returns the number of bytes read

+ 10 - 2
codec/encode.go

@@ -66,6 +66,14 @@ type encDriverAsis interface {
 	EncodeAsis(v []byte)
 }
 
+type encodeError struct {
+	codecError
+}
+
+func (e encodeError) Error() string {
+	return fmt.Sprintf("%s encode error: %v", e.name, e.err)
+}
+
 type encDriverNoopContainerWriter struct{}
 
 func (encDriverNoopContainerWriter) WriteArrayStart(length int) {}
@@ -1436,6 +1444,6 @@ func (e *Encoder) rawBytes(vv Raw) {
 	e.asis(v)
 }
 
-func (e *Encoder) wrapErrstr(v interface{}, err *error) {
-	*err = fmt.Errorf("%s encode error: %v", e.hh.Name(), v)
+func (e *Encoder) wrapErr(v interface{}, err *error) {
+	*err = encodeError{codecError{name: e.hh.Name(), err: v}}
 }

+ 34 - 10
codec/helper.go

@@ -295,6 +295,30 @@ type isZeroer interface {
 	IsZero() bool
 }
 
+type codecError struct {
+	name string
+	err  interface{}
+}
+
+func (e codecError) Cause() error {
+	switch xerr := e.err.(type) {
+	case nil:
+		return nil
+	case error:
+		return xerr
+	case string:
+		return errors.New(xerr)
+	case fmt.Stringer:
+		return errors.New(xerr.String())
+	default:
+		return fmt.Errorf("%v", e.err)
+	}
+}
+
+func (e codecError) Error() string {
+	return fmt.Sprintf("%s error: %v", e.name, e.err)
+}
+
 // type byteAccepter func(byte) bool
 
 var (
@@ -1597,7 +1621,7 @@ func isEmptyStruct(v reflect.Value, tinfos *TypeInfos, deref, checkStruct bool)
 // 	return t
 // }
 
-func panicToErr(h errstrDecorator, err *error) {
+func panicToErr(h errDecorator, err *error) {
 	// Note: This method MUST be called directly from defer i.e. defer panicToErr ...
 	// else it seems the recover is not fully handled
 	if recoverPanicToErr {
@@ -1609,7 +1633,7 @@ func panicToErr(h errstrDecorator, err *error) {
 	}
 }
 
-func panicValToErr(h errstrDecorator, v interface{}, err *error) {
+func panicValToErr(h errDecorator, v interface{}, err *error) {
 	switch xerr := v.(type) {
 	case nil:
 	case error:
@@ -1619,18 +1643,18 @@ func panicValToErr(h errstrDecorator, v interface{}, err *error) {
 			// treat as special (bubble up)
 			*err = xerr
 		default:
-			h.wrapErrstr(xerr.Error(), err)
+			h.wrapErr(xerr, err)
 		}
 	case string:
 		if xerr != "" {
-			h.wrapErrstr(xerr, err)
+			h.wrapErr(xerr, err)
 		}
 	case fmt.Stringer:
 		if xerr != nil {
-			h.wrapErrstr(xerr.String(), err)
+			h.wrapErr(xerr, err)
 		}
 	default:
-		h.wrapErrstr(v, err)
+		h.wrapErr(v, err)
 	}
 }
 
@@ -2386,13 +2410,13 @@ func (panicHdl) errorf(format string, params ...interface{}) {
 	}
 }
 
-type errstrDecorator interface {
-	wrapErrstr(interface{}, *error)
+type errDecorator interface {
+	wrapErr(in interface{}, out *error)
 }
 
-type errstrDecoratorDef struct{}
+type errDecoratorDef struct{}
 
-func (errstrDecoratorDef) wrapErrstr(v interface{}, e *error) { *e = fmt.Errorf("%v", v) }
+func (errDecoratorDef) wrapErr(v interface{}, e *error) { *e = fmt.Errorf("%v", v) }
 
 type must struct{}