--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package jsonrpc2
+
+import (
+ "encoding/json"
+ "fmt"
+
+ errors "golang.org/x/xerrors"
+)
+
+// Message is the interface to all jsonrpc2 message types.
+// They share no common functionality, but are a closed set of concrete types
+// that are allowed to implement this interface. The message types are *Call,
+// *Notification and *Response.
+type Message interface {
+ // isJSONRPC2Message is used to make the set of message implementations a
+ // closed set.
+ isJSONRPC2Message()
+}
+
+// Request is the shared interface to jsonrpc2 messages that request
+// a method be invoked.
+// The request types are a closed set of *Call and *Notification.
+type Request interface {
+ Message
+ // Method is a string containing the method name to invoke.
+ Method() string
+ // Params is either a struct or an array with the parameters of the method.
+ Params() json.RawMessage
+ // isJSONRPC2Request is used to make the set of request implementations closed.
+ isJSONRPC2Request()
+}
+
+// Notification is a request for which a response cannot occur, and as such
+// it has not ID.
+type Notification struct {
+ // Method is a string containing the method name to invoke.
+ method string
+ params json.RawMessage
+}
+
+// Call is a request that expects a response.
+// The response will have a matching ID.
+type Call struct {
+ // Method is a string containing the method name to invoke.
+ method string
+ // Params is either a struct or an array with the parameters of the method.
+ params json.RawMessage
+ // id of this request, used to tie the Response back to the request.
+ id ID
+}
+
+// Response is a reply to a Call.
+// It will have the same ID as the call it is a response to.
+type Response struct {
+ // result is the content of the response.
+ result json.RawMessage
+ // err is set only if the call failed.
+ err error
+ // ID of the request this is a response to.
+ id ID
+}
+
+// NewNotification constructs a new Notification message for the supplied
+// method and parameters.
+func NewNotification(method string, params interface{}) (*Notification, error) {
+ p, merr := marshalToRaw(params)
+ return &Notification{method: method, params: p}, merr
+}
+
+func (msg *Notification) Method() string { return msg.method }
+func (msg *Notification) Params() json.RawMessage { return msg.params }
+func (msg *Notification) isJSONRPC2Message() {}
+func (msg *Notification) isJSONRPC2Request() {}
+
+func (n *Notification) MarshalJSON() ([]byte, error) {
+ msg := wireRequest{Method: n.method, Params: &n.params}
+ data, err := json.Marshal(msg)
+ if err != nil {
+ return data, fmt.Errorf("marshaling notification: %w", err)
+ }
+ return data, nil
+}
+
+func (n *Notification) UnmarshalJSON(data []byte) error {
+ msg := wireRequest{}
+ if err := json.Unmarshal(data, &msg); err != nil {
+ return fmt.Errorf("unmarshaling notification: %w", err)
+ }
+ n.method = msg.Method
+ if msg.Params != nil {
+ n.params = *msg.Params
+ }
+ return nil
+}
+
+// NewCall constructs a new Call message for the supplied ID, method and
+// parameters.
+func NewCall(id ID, method string, params interface{}) (*Call, error) {
+ p, merr := marshalToRaw(params)
+ return &Call{id: id, method: method, params: p}, merr
+}
+
+func (msg *Call) Method() string { return msg.method }
+func (msg *Call) Params() json.RawMessage { return msg.params }
+func (msg *Call) ID() ID { return msg.id }
+func (msg *Call) isJSONRPC2Message() {}
+func (msg *Call) isJSONRPC2Request() {}
+
+func (c *Call) MarshalJSON() ([]byte, error) {
+ msg := wireRequest{Method: c.method, Params: &c.params, ID: &c.id}
+ data, err := json.Marshal(msg)
+ if err != nil {
+ return data, fmt.Errorf("marshaling call: %w", err)
+ }
+ return data, nil
+}
+
+func (c *Call) UnmarshalJSON(data []byte) error {
+ msg := wireRequest{}
+ if err := json.Unmarshal(data, &msg); err != nil {
+ return fmt.Errorf("unmarshaling call: %w", err)
+ }
+ c.method = msg.Method
+ if msg.Params != nil {
+ c.params = *msg.Params
+ }
+ if msg.ID != nil {
+ c.id = *msg.ID
+ }
+ return nil
+}
+
+// NewResponse constructs a new Response message that is a reply to the
+// supplied. If err is set result may be ignored.
+func NewResponse(id ID, result interface{}, err error) (*Response, error) {
+ r, merr := marshalToRaw(result)
+ return &Response{id: id, result: r, err: err}, merr
+}
+
+func (msg *Response) ID() ID { return msg.id }
+func (msg *Response) Result() json.RawMessage { return msg.result }
+func (msg *Response) Err() error { return msg.err }
+func (msg *Response) isJSONRPC2Message() {}
+
+func (r *Response) MarshalJSON() ([]byte, error) {
+ msg := &wireResponse{Error: toWireError(r.err), ID: &r.id}
+ if msg.Error == nil {
+ msg.Result = &r.result
+ }
+ data, err := json.Marshal(msg)
+ if err != nil {
+ return data, fmt.Errorf("marshaling notification: %w", err)
+ }
+ return data, nil
+}
+
+func toWireError(err error) *wireError {
+ if err == nil {
+ // no error, the response is complete
+ return nil
+ }
+ if err, ok := err.(*wireError); ok {
+ // already a wire error, just use it
+ return err
+ }
+ result := &wireError{Message: err.Error()}
+ var wrapped *wireError
+ if errors.As(err, &wrapped) {
+ // if we wrapped a wire error, keep the code from the wrapped error
+ // but the message from the outer error
+ result.Code = wrapped.Code
+ }
+ return result
+}
+
+func (r *Response) UnmarshalJSON(data []byte) error {
+ msg := wireResponse{}
+ if err := json.Unmarshal(data, &msg); err != nil {
+ return fmt.Errorf("unmarshaling jsonrpc response: %w", err)
+ }
+ if msg.Result != nil {
+ r.result = *msg.Result
+ }
+ if msg.Error != nil {
+ r.err = msg.Error
+ }
+ if msg.ID != nil {
+ r.id = *msg.ID
+ }
+ return nil
+}
+
+func DecodeMessage(data []byte) (Message, error) {
+ msg := wireCombined{}
+ if err := json.Unmarshal(data, &msg); err != nil {
+ return nil, fmt.Errorf("unmarshaling jsonrpc message: %w", err)
+ }
+ if msg.Method == "" {
+ // no method, should be a response
+ if msg.ID == nil {
+ return nil, ErrInvalidRequest
+ }
+ response := &Response{id: *msg.ID}
+ if msg.Error != nil {
+ response.err = msg.Error
+ }
+ if msg.Result != nil {
+ response.result = *msg.Result
+ }
+ return response, nil
+ }
+ // has a method, must be a request
+ if msg.ID == nil {
+ // request with no ID is a notify
+ notify := &Notification{method: msg.Method}
+ if msg.Params != nil {
+ notify.params = *msg.Params
+ }
+ return notify, nil
+ }
+ // request with an ID, must be a call
+ call := &Call{method: msg.Method, id: *msg.ID}
+ if msg.Params != nil {
+ call.params = *msg.Params
+ }
+ return call, nil
+}
+
+func marshalToRaw(obj interface{}) (json.RawMessage, error) {
+ data, err := json.Marshal(obj)
+ if err != nil {
+ return json.RawMessage{}, err
+ }
+ return json.RawMessage(data), nil
+}