diff --git a/error.go b/error.go index 6a5bfe3..3e7b33a 100644 --- a/error.go +++ b/error.go @@ -34,11 +34,15 @@ func NewError(err error) *Error { // retrieve error details errType := reflect.TypeOf(err) + errElem := errType + if errType.Kind() == reflect.Ptr { + errElem = errType.Elem() + } inst := &Error{ Msg: err.Error(), Type: errType.String(), - Pkg: errType.Elem().PkgPath(), + Pkg: errElem.PkgPath(), Data: err, } @@ -80,9 +84,9 @@ func (e *Error) Format(s fmt.State, verb rune) { switch verb { case 'v': if s.Flag('+') { - fmt.Fprintf(s, "%s.(%s)\n", e.Pkg, e.Type) + _, _ = fmt.Fprintf(s, "%s.(%s)\n", e.Pkg, e.Type) if e.Data != nil { - fmt.Fprintf(s, "Data: %v\n", e.Data) + _, _ = fmt.Fprintf(s, "Data: %v\n", e.Data) } } fallthrough @@ -106,10 +110,14 @@ func (e *Error) Is(err error) bool { } errType := reflect.TypeOf(err) + errElem := errType + if errType.Kind() == reflect.Ptr { + errElem = errType.Elem() + } if e.Msg == err.Error() && errType.String() == e.Type && - errType.Elem().PkgPath() == e.Pkg { + errElem.PkgPath() == e.Pkg { return true } diff --git a/example/errors/handler/backend/handler.go b/example/errors/handler/backend/handler.go index 31443d1..3f86065 100644 --- a/example/errors/handler/backend/handler.go +++ b/example/errors/handler/backend/handler.go @@ -10,6 +10,14 @@ import ( type ( ScalarError string + StructError struct { + Msg string + Map map[string]string + Slice []string + Struct struct { + A string + } + } CustomError struct { Msg string Map map[string]string @@ -33,6 +41,19 @@ func (e *ScalarError) Error() string { return string(*e) } +func NewStructError(msg string) StructError { + return StructError{ + Msg: msg, + Map: map[string]string{"a": "b"}, + Slice: []string{"a", "b"}, + Struct: struct{ A string }{A: "b"}, + } +} + +func (e StructError) Error() string { + return e.Msg +} + func NewCustomError(msg string) *CustomError { return &CustomError{ Msg: msg, @@ -86,6 +107,10 @@ func (h *Handler) TypedError(w http.ResponseWriter, r *http.Request) (e error) { return ErrTyped } +func (h *Handler) StructError(w http.ResponseWriter, r *http.Request) (e error) { + return NewStructError("struct error") +} + func (h *Handler) ScalarError(w http.ResponseWriter, r *http.Request) (e error) { return NewScalarError(ScalarErrorOne) } diff --git a/example/errors/main.go b/example/errors/main.go index e0317d0..3f8174b 100644 --- a/example/errors/main.go +++ b/example/errors/main.go @@ -147,6 +147,28 @@ func call() { } } + { + fmt.Println("--- StructError ----------------") + var structErr backend.StructError + var gotsrpcErr *gotsrpc.Error + serviceErr, err := c.StructError(ctx) + if err != nil { + panic("client error should be nil") + } else if serviceErr == nil { + panic("service error should not be nil") + } else if serviceErr != nil { + fmt.Printf("%s\n", serviceErr) + fmt.Printf("%q\n", serviceErr) + fmt.Printf("%+v\n", serviceErr) + } + if errors.As(serviceErr, &gotsrpcErr) { + fmt.Println(gotsrpcErr) + } + if errors.As(serviceErr, &structErr) { + fmt.Println(structErr) + } + } + { fmt.Println("--- CustomError ----------------") var customErr *backend.CustomError diff --git a/example/errors/service/backend/gotsrpc_gen.go b/example/errors/service/backend/gotsrpc_gen.go index efe58fb..3a7f7aa 100644 --- a/example/errors/service/backend/gotsrpc_gen.go +++ b/example/errors/service/backend/gotsrpc_gen.go @@ -17,6 +17,7 @@ const ( ServiceGoTSRPCProxyScalar = "Scalar" ServiceGoTSRPCProxyScalarError = "ScalarError" ServiceGoTSRPCProxyStruct = "Struct" + ServiceGoTSRPCProxyStructError = "StructError" ServiceGoTSRPCProxyTypedCustomError = "TypedCustomError" ServiceGoTSRPCProxyTypedError = "TypedError" ServiceGoTSRPCProxyTypedScalarError = "TypedScalarError" @@ -164,6 +165,24 @@ func (p *ServiceGoTSRPCProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) } gotsrpc.Monitor(w, r, args, rets, callStats) return + case ServiceGoTSRPCProxyStructError: + var ( + args []interface{} + rets []interface{} + ) + executionStart := time.Now() + rw := gotsrpc.ResponseWriter{ResponseWriter: w} + structErrorE := p.service.StructError(&rw, r) + callStats.Execution = time.Since(executionStart) + if rw.Status() == http.StatusOK { + rets = []interface{}{structErrorE} + if err := gotsrpc.Reply(rets, callStats, r, w); err != nil { + gotsrpc.ErrorCouldNotReply(w) + return + } + } + gotsrpc.Monitor(w, r, args, rets, callStats) + return case ServiceGoTSRPCProxyTypedCustomError: var ( args []interface{} diff --git a/example/errors/service/backend/gotsrpcclient_gen.go b/example/errors/service/backend/gotsrpcclient_gen.go index 928d243..c67658b 100644 --- a/example/errors/service/backend/gotsrpcclient_gen.go +++ b/example/errors/service/backend/gotsrpcclient_gen.go @@ -17,6 +17,7 @@ type ServiceGoTSRPCClient interface { Scalar(ctx go_context.Context) (e *ScalarError, clientErr error) ScalarError(ctx go_context.Context) (e error, clientErr error) Struct(ctx go_context.Context) (e *StructError, clientErr error) + StructError(ctx go_context.Context) (e error, clientErr error) TypedCustomError(ctx go_context.Context) (e error, clientErr error) TypedError(ctx go_context.Context) (e error, clientErr error) TypedScalarError(ctx go_context.Context) (e error, clientErr error) @@ -105,6 +106,16 @@ func (tsc *HTTPServiceGoTSRPCClient) Struct(ctx go_context.Context) (e *StructEr return } +func (tsc *HTTPServiceGoTSRPCClient) StructError(ctx go_context.Context) (e error, clientErr error) { + args := []interface{}{} + reply := []interface{}{&e} + clientErr = tsc.Client.Call(ctx, tsc.URL, tsc.EndPoint, "StructError", args, reply) + if clientErr != nil { + clientErr = pkg_errors.WithMessage(clientErr, "failed to call backend.ServiceGoTSRPCProxy StructError") + } + return +} + func (tsc *HTTPServiceGoTSRPCClient) TypedCustomError(ctx go_context.Context) (e error, clientErr error) { args := []interface{}{} reply := []interface{}{&e} diff --git a/example/errors/service/backend/service.go b/example/errors/service/backend/service.go index 7fe19bf..879a46c 100644 --- a/example/errors/service/backend/service.go +++ b/example/errors/service/backend/service.go @@ -54,6 +54,7 @@ type Service interface { Scalar(w http.ResponseWriter, r *http.Request) (e *ScalarError) MultiScalar(w http.ResponseWriter, r *http.Request) (e *MultiScalar) Struct(w http.ResponseWriter, r *http.Request) (e *StructError) + StructError(w http.ResponseWriter, r *http.Request) (e error) TypedError(w http.ResponseWriter, r *http.Request) (e error) ScalarError(w http.ResponseWriter, r *http.Request) (e error) CustomError(w http.ResponseWriter, r *http.Request) (e error) diff --git a/gotsrpc.go b/gotsrpc.go index fa18217..01345a7 100644 --- a/gotsrpc.go +++ b/gotsrpc.go @@ -120,7 +120,7 @@ func Reply(response []interface{}, stats *CallStats, r *http.Request, w http.Res if len(response) > 0 { errResp := response[len(response)-1] if v, ok := errResp.(error); ok && v != nil { - if !reflect.ValueOf(v).IsNil() { + if !reflect.ValueOf(v).IsZero() { stats.ErrorCode = 1 stats.ErrorType = fmt.Sprintf("%T", v) stats.ErrorMessage = v.Error() diff --git a/transport.go b/transport.go index ea07faf..8ea7d95 100644 --- a/transport.go +++ b/transport.go @@ -33,7 +33,7 @@ var msgpackClientHandle = &clientHandle{ beforeEncodeReply: func(resp *[]interface{}) error { for k, v := range *resp { if e, ok := v.(error); ok { - if r := reflect.ValueOf(e); !r.IsNil() { + if r := reflect.ValueOf(e); !r.IsZero() { (*resp)[k] = NewError(e) } } @@ -43,7 +43,11 @@ var msgpackClientHandle = &clientHandle{ beforeDecodeReply: func(reply []interface{}) ([]interface{}, error) { ret := make([]interface{}, len(reply)) for k, v := range reply { - if reflect.TypeOf(v).Elem().Implements(errorType) { + val := reflect.TypeOf(v) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if val.Implements(errorType) { var e *Error ret[k] = e } else {