From 37bcf3bd3f09c521924595fda0d2a7f0a73dc3a4 Mon Sep 17 00:00:00 2001 From: "M. J. Fromberger" Date: Wed, 13 Oct 2021 17:36:25 -0700 Subject: [PATCH] Remove the DataErrorf constructor. Make Errorf return the *jrpc2.Error value concretely, and equip that type with a WithData method. This permits the reuse of base errors with different data. --- base.go | 2 +- error.go | 30 ++++++++++++++---------------- internal_test.go | 2 +- jrpc2_test.go | 2 +- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/base.go b/base.go index 9b2bd6c..9271564 100644 --- a/base.go +++ b/base.go @@ -360,7 +360,7 @@ func (j *jmessage) parseJSON(data []byte) error { // Report an error for extraneous fields. if j.err == nil && len(extra) != 0 { - j.err = DataErrorf(code.InvalidRequest, extra, "extra fields in request") + j.err = Errorf(code.InvalidRequest, "extra fields in request").WithData(extra) } return nil } diff --git a/error.go b/error.go index 561a799..528c340 100644 --- a/error.go +++ b/error.go @@ -26,6 +26,18 @@ func (e Error) MarshalJSON() ([]byte, error) { return json.Marshal(jerror{C: int32(e.Code), M: e.Message, D: e.Data}) } +// WithData marshals v as JSON and constructs a copy of e whose Data field +// includes the result. If v == nil or if marshaling v fails, e is returned +// without modification. +func (e *Error) WithData(v interface{}) *Error { + if v == nil { + return e + } else if data, err := json.Marshal(v); err == nil { + return &Error{Code: e.Code, Message: e.Message, Data: data} + } + return e +} + // UnmarshalJSON implements the json.Unmarshaler interface for Error values. func (e *Error) UnmarshalJSON(data []byte) error { var v jerror @@ -62,20 +74,6 @@ var ErrConnClosed = errors.New("client connection is closed") // Errorf returns an error value of concrete type *Error having the specified // code and formatted message string. -// It is shorthand for DataErrorf(code, nil, msg, args...) -func Errorf(code code.Code, msg string, args ...interface{}) error { - return DataErrorf(code, nil, msg, args...) -} - -// DataErrorf returns an error value of concrete type *Error having the -// specified code, error data, and formatted message string. -// If v == nil this behaves identically to Errorf(code, msg, args...). -func DataErrorf(code code.Code, v interface{}, msg string, args ...interface{}) error { - e := &Error{Code: code, Message: fmt.Sprintf(msg, args...)} - if v != nil { - if data, err := json.Marshal(v); err == nil { - e.Data = data - } - } - return e +func Errorf(code code.Code, msg string, args ...interface{}) *Error { + return &Error{Code: code, Message: fmt.Sprintf(msg, args...)} } diff --git a/internal_test.go b/internal_test.go index 26c620f..bf081ea 100644 --- a/internal_test.go +++ b/internal_test.go @@ -292,7 +292,7 @@ func TestMarshalResponse(t *testing.T) { }{ {"", nil, "", `{"jsonrpc":"2.0"}`}, {"null", nil, "", `{"jsonrpc":"2.0","id":null}`}, - {"123", Errorf(code.ParseError, "failed").(*Error), "", + {"123", Errorf(code.ParseError, "failed"), "", `{"jsonrpc":"2.0","id":123,"error":{"code":-32700,"message":"failed"}}`}, {"456", nil, `{"ok":true,"values":[4,5,6]}`, `{"jsonrpc":"2.0","id":456,"result":{"ok":true,"values":[4,5,6]}}`}, diff --git a/jrpc2_test.go b/jrpc2_test.go index e594e31..f3e9c6a 100644 --- a/jrpc2_test.go +++ b/jrpc2_test.go @@ -435,7 +435,7 @@ func TestError_withData(t *testing.T) { const errMessage = "error thingy" loc := server.NewLocal(handler.Map{ "Err": handler.New(func(_ context.Context) (int, error) { - return 17, jrpc2.DataErrorf(errCode, json.RawMessage(errData), errMessage) + return 17, jrpc2.Errorf(errCode, errMessage).WithData(json.RawMessage(errData)) }), "Push": handler.New(func(ctx context.Context) (bool, error) { return false, jrpc2.ServerFromContext(ctx).Notify(ctx, "PushBack", nil)