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
5package jsonrpc2
6
7import (
8	"encoding/json"
9	"fmt"
10	"strconv"
11)
12
13// this file contains the go forms of the wire specification
14// see http://www.jsonrpc.org/specification for details
15
16const (
17	// CodeUnknownError should be used for all non coded errors.
18	CodeUnknownError = -32001
19	// CodeParseError is used when invalid JSON was received by the server.
20	CodeParseError = -32700
21	//CodeInvalidRequest is used when the JSON sent is not a valid Request object.
22	CodeInvalidRequest = -32600
23	// CodeMethodNotFound should be returned by the handler when the method does
24	// not exist / is not available.
25	CodeMethodNotFound = -32601
26	// CodeInvalidParams should be returned by the handler when method
27	// parameter(s) were invalid.
28	CodeInvalidParams = -32602
29	// CodeInternalError is not currently returned but defined for completeness.
30	CodeInternalError = -32603
31)
32
33// Request is sent to a server to represent a Call or Notify operaton.
34type Request struct {
35	// VersionTag is always encoded as the string "2.0"
36	VersionTag VersionTag `json:"jsonrpc"`
37	// Method is a string containing the method name to invoke.
38	Method string `json:"method"`
39	// Params is either a struct or an array with the parameters of the method.
40	Params *json.RawMessage `json:"params,omitempty"`
41	// The id of this request, used to tie the Response back to the request.
42	// Will be either a string or a number. If not set, the Request is a notify,
43	// and no response is possible.
44	ID *ID `json:"id,omitempty"`
45}
46
47// Response is a reply to a Request.
48// It will always have the ID field set to tie it back to a request, and will
49// have either the Result or Error fields set depending on whether it is a
50// success or failure response.
51type Response struct {
52	// VersionTag is always encoded as the string "2.0"
53	VersionTag VersionTag `json:"jsonrpc"`
54	// Result is the response value, and is required on success.
55	Result *json.RawMessage `json:"result,omitempty"`
56	// Error is a structured error response if the call fails.
57	Error *Error `json:"error,omitempty"`
58	// ID must be set and is the identifier of the Request this is a response to.
59	ID *ID `json:"id,omitempty"`
60}
61
62// Error represents a structured error in a Response.
63type Error struct {
64	// Code is an error code indicating the type of failure.
65	Code int64 `json:"code"`
66	// Message is a short description of the error.
67	Message string `json:"message"`
68	// Data is optional structured data containing additional information about the error.
69	Data *json.RawMessage `json:"data"`
70}
71
72// VersionTag is a special 0 sized struct that encodes as the jsonrpc version
73// tag.
74// It will fail during decode if it is not the correct version tag in the
75// stream.
76type VersionTag struct{}
77
78// ID is a Request identifier.
79// Only one of either the Name or Number members will be set, using the
80// number form if the Name is the empty string.
81type ID struct {
82	Name   string
83	Number int64
84}
85
86// IsNotify returns true if this request is a notification.
87func (r *Request) IsNotify() bool {
88	return r.ID == nil
89}
90
91func (err *Error) Error() string {
92	if err == nil {
93		return ""
94	}
95	return err.Message
96}
97
98func (VersionTag) MarshalJSON() ([]byte, error) {
99	return json.Marshal("2.0")
100}
101
102func (VersionTag) UnmarshalJSON(data []byte) error {
103	version := ""
104	if err := json.Unmarshal(data, &version); err != nil {
105		return err
106	}
107	if version != "2.0" {
108		return fmt.Errorf("Invalid RPC version %v", version)
109	}
110	return nil
111}
112
113// String returns a string representation of the ID.
114// The representation is non ambiguous, string forms are quoted, number forms
115// are preceded by a #
116func (id *ID) String() string {
117	if id == nil {
118		return ""
119	}
120	if id.Name != "" {
121		return strconv.Quote(id.Name)
122	}
123	return "#" + strconv.FormatInt(id.Number, 10)
124}
125
126func (id *ID) MarshalJSON() ([]byte, error) {
127	if id.Name != "" {
128		return json.Marshal(id.Name)
129	}
130	return json.Marshal(id.Number)
131}
132
133func (id *ID) UnmarshalJSON(data []byte) error {
134	*id = ID{}
135	if err := json.Unmarshal(data, &id.Number); err == nil {
136		return nil
137	}
138	return json.Unmarshal(data, &id.Name)
139}
140