golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/internal/jsonrpc2/messages.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package jsonrpc2
     6  
     7  import (
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  )
    12  
    13  // Message is the interface to all jsonrpc2 message types.
    14  // They share no common functionality, but are a closed set of concrete types
    15  // that are allowed to implement this interface. The message types are *Call,
    16  // *Notification and *Response.
    17  type Message interface {
    18  	// isJSONRPC2Message is used to make the set of message implementations a
    19  	// closed set.
    20  	isJSONRPC2Message()
    21  }
    22  
    23  // Request is the shared interface to jsonrpc2 messages that request
    24  // a method be invoked.
    25  // The request types are a closed set of *Call and *Notification.
    26  type Request interface {
    27  	Message
    28  	// Method is a string containing the method name to invoke.
    29  	Method() string
    30  	// Params is an JSON value (object, array, null, or "") with the parameters of the method.
    31  	Params() json.RawMessage
    32  	// isJSONRPC2Request is used to make the set of request implementations closed.
    33  	isJSONRPC2Request()
    34  }
    35  
    36  // Notification is a request for which a response cannot occur, and as such
    37  // it has not ID.
    38  type Notification struct {
    39  	// Method is a string containing the method name to invoke.
    40  	method string
    41  	params json.RawMessage
    42  }
    43  
    44  // Call is a request that expects a response.
    45  // The response will have a matching ID.
    46  type Call struct {
    47  	// Method is a string containing the method name to invoke.
    48  	method string
    49  	// Params is a JSON value (object, array, null, or "") with the parameters of the method.
    50  	params json.RawMessage
    51  	// id of this request, used to tie the Response back to the request.
    52  	id ID
    53  }
    54  
    55  // Response is a reply to a Call.
    56  // It will have the same ID as the call it is a response to.
    57  type Response struct {
    58  	// result is the content of the response.
    59  	result json.RawMessage
    60  	// err is set only if the call failed.
    61  	err error
    62  	// ID of the request this is a response to.
    63  	id ID
    64  }
    65  
    66  // NewNotification constructs a new Notification message for the supplied
    67  // method and parameters.
    68  func NewNotification(method string, params interface{}) (*Notification, error) {
    69  	p, merr := marshalToRaw(params)
    70  	return &Notification{method: method, params: p}, merr
    71  }
    72  
    73  func (msg *Notification) Method() string          { return msg.method }
    74  func (msg *Notification) Params() json.RawMessage { return msg.params }
    75  func (msg *Notification) isJSONRPC2Message()      {}
    76  func (msg *Notification) isJSONRPC2Request()      {}
    77  
    78  func (n *Notification) MarshalJSON() ([]byte, error) {
    79  	msg := wireRequest{Method: n.method, Params: &n.params}
    80  	data, err := json.Marshal(msg)
    81  	if err != nil {
    82  		return data, fmt.Errorf("marshaling notification: %w", err)
    83  	}
    84  	return data, nil
    85  }
    86  
    87  func (n *Notification) UnmarshalJSON(data []byte) error {
    88  	msg := wireRequest{}
    89  	if err := json.Unmarshal(data, &msg); err != nil {
    90  		return fmt.Errorf("unmarshaling notification: %w", err)
    91  	}
    92  	n.method = msg.Method
    93  	if msg.Params != nil {
    94  		n.params = *msg.Params
    95  	}
    96  	return nil
    97  }
    98  
    99  // NewCall constructs a new Call message for the supplied ID, method and
   100  // parameters.
   101  func NewCall(id ID, method string, params interface{}) (*Call, error) {
   102  	p, merr := marshalToRaw(params)
   103  	return &Call{id: id, method: method, params: p}, merr
   104  }
   105  
   106  func (msg *Call) Method() string          { return msg.method }
   107  func (msg *Call) Params() json.RawMessage { return msg.params }
   108  func (msg *Call) ID() ID                  { return msg.id }
   109  func (msg *Call) isJSONRPC2Message()      {}
   110  func (msg *Call) isJSONRPC2Request()      {}
   111  
   112  func (c *Call) MarshalJSON() ([]byte, error) {
   113  	msg := wireRequest{Method: c.method, Params: &c.params, ID: &c.id}
   114  	data, err := json.Marshal(msg)
   115  	if err != nil {
   116  		return data, fmt.Errorf("marshaling call: %w", err)
   117  	}
   118  	return data, nil
   119  }
   120  
   121  func (c *Call) UnmarshalJSON(data []byte) error {
   122  	msg := wireRequest{}
   123  	if err := json.Unmarshal(data, &msg); err != nil {
   124  		return fmt.Errorf("unmarshaling call: %w", err)
   125  	}
   126  	c.method = msg.Method
   127  	if msg.Params != nil {
   128  		c.params = *msg.Params
   129  	}
   130  	if msg.ID != nil {
   131  		c.id = *msg.ID
   132  	}
   133  	return nil
   134  }
   135  
   136  // NewResponse constructs a new Response message that is a reply to the
   137  // supplied. If err is set result may be ignored.
   138  func NewResponse(id ID, result interface{}, err error) (*Response, error) {
   139  	r, merr := marshalToRaw(result)
   140  	return &Response{id: id, result: r, err: err}, merr
   141  }
   142  
   143  func (msg *Response) ID() ID                  { return msg.id }
   144  func (msg *Response) Result() json.RawMessage { return msg.result }
   145  func (msg *Response) Err() error              { return msg.err }
   146  func (msg *Response) isJSONRPC2Message()      {}
   147  
   148  func (r *Response) MarshalJSON() ([]byte, error) {
   149  	msg := &wireResponse{Error: toWireError(r.err), ID: &r.id}
   150  	if msg.Error == nil {
   151  		msg.Result = &r.result
   152  	}
   153  	data, err := json.Marshal(msg)
   154  	if err != nil {
   155  		return data, fmt.Errorf("marshaling notification: %w", err)
   156  	}
   157  	return data, nil
   158  }
   159  
   160  func toWireError(err error) *WireError {
   161  	if err == nil {
   162  		// no error, the response is complete
   163  		return nil
   164  	}
   165  	if err, ok := err.(*WireError); ok {
   166  		// already a wire error, just use it
   167  		return err
   168  	}
   169  	result := &WireError{Message: err.Error()}
   170  	var wrapped *WireError
   171  	if errors.As(err, &wrapped) {
   172  		// if we wrapped a wire error, keep the code from the wrapped error
   173  		// but the message from the outer error
   174  		result.Code = wrapped.Code
   175  	}
   176  	return result
   177  }
   178  
   179  func (r *Response) UnmarshalJSON(data []byte) error {
   180  	msg := wireResponse{}
   181  	if err := json.Unmarshal(data, &msg); err != nil {
   182  		return fmt.Errorf("unmarshaling jsonrpc response: %w", err)
   183  	}
   184  	if msg.Result != nil {
   185  		r.result = *msg.Result
   186  	}
   187  	if msg.Error != nil {
   188  		r.err = msg.Error
   189  	}
   190  	if msg.ID != nil {
   191  		r.id = *msg.ID
   192  	}
   193  	return nil
   194  }
   195  
   196  func DecodeMessage(data []byte) (Message, error) {
   197  	msg := wireCombined{}
   198  	if err := json.Unmarshal(data, &msg); err != nil {
   199  		return nil, fmt.Errorf("unmarshaling jsonrpc message: %w", err)
   200  	}
   201  	if msg.Method == "" {
   202  		// no method, should be a response
   203  		if msg.ID == nil {
   204  			return nil, ErrInvalidRequest
   205  		}
   206  		response := &Response{id: *msg.ID}
   207  		if msg.Error != nil {
   208  			response.err = msg.Error
   209  		}
   210  		if msg.Result != nil {
   211  			response.result = *msg.Result
   212  		}
   213  		return response, nil
   214  	}
   215  	// has a method, must be a request
   216  	if msg.ID == nil {
   217  		// request with no ID is a notify
   218  		notify := &Notification{method: msg.Method}
   219  		if msg.Params != nil {
   220  			notify.params = *msg.Params
   221  		}
   222  		return notify, nil
   223  	}
   224  	// request with an ID, must be a call
   225  	call := &Call{method: msg.Method, id: *msg.ID}
   226  	if msg.Params != nil {
   227  		call.params = *msg.Params
   228  	}
   229  	return call, nil
   230  }
   231  
   232  func marshalToRaw(obj interface{}) (json.RawMessage, error) {
   233  	data, err := json.Marshal(obj)
   234  	if err != nil {
   235  		return json.RawMessage{}, err
   236  	}
   237  	return json.RawMessage(data), nil
   238  }