|
5 | 5 | package jsonrpc2
|
6 | 6 |
|
7 | 7 | import (
|
8 |
| - "encoding/json" |
| 8 | + "context" |
| 9 | + "fmt" |
| 10 | + "sync" |
9 | 11 | )
|
10 | 12 |
|
11 |
| -// ID is a Request identifier. |
12 |
| -// Only one of either the Name or Number members will be set, using the |
13 |
| -// number form if the Name is the empty string. |
14 |
| -type ID struct { |
15 |
| - Name string |
16 |
| - Number int64 |
17 |
| -} |
18 |
| - |
19 |
| -// Message is a general message as defined by JSON-RPC. The language server protocol always uses "2.0" as the jsonrpc version. |
20 |
| -type Message struct { |
21 |
| - JSONRPC string `json:"jsonrpc"` |
22 |
| -} |
| 13 | +type Interface interface { |
| 14 | + Call(ctx context.Context, method string, params, result interface{}) error |
23 | 15 |
|
24 |
| -// Request is a request message to describe a request between the client and the server. |
25 |
| -// Every processed request must send a response back to the sender of the request. |
26 |
| -type Request struct { |
27 |
| - Message |
| 16 | + Reply(ctx context.Context, req *Request, result interface{}, err error) error |
28 | 17 |
|
29 |
| - // The request id. |
30 |
| - ID *ID `json:"id"` |
| 18 | + Notify(ctx context.Context, method string, params interface{}) error |
31 | 19 |
|
32 |
| - // The method to be invoked. |
33 |
| - Method string `json:"method"` |
| 20 | + Cancel(id ID) |
34 | 21 |
|
35 |
| - // The method's params. |
36 |
| - Params *json.RawMessage `json:"params,omitempty"` |
| 22 | + Wait(ctx context.Context) error |
37 | 23 | }
|
38 | 24 |
|
39 |
| -// Response is a response ressage sent as a result of a request. |
40 |
| -// If a request doesn't provide a result value the receiver of a request still needs to return a response message to |
41 |
| -// conform to the JSON RPC specification. |
42 |
| -// The result property of the ResponseMessage should be set to null in this case to signal a successful request. |
43 |
| -type Response struct { |
44 |
| - Message |
45 |
| - |
46 |
| - // The error object in case a request fails. |
47 |
| - Error *ResponseError `json:"error,omitempty"` |
48 |
| - |
49 |
| - // The request id. |
50 |
| - ID *ID `json:"id"` |
| 25 | +// Conn is a JSON RPC 2 client server connection. |
| 26 | +// Conn is bidirectional; it does not have a designated server or client end. |
| 27 | +type Conn struct { |
| 28 | + handle Handler |
| 29 | + cancel Canceler |
| 30 | + stream Stream |
| 31 | + done chan struct{} |
| 32 | + err error |
| 33 | + seq int64 // must only be accessed using atomic operations |
| 34 | + pendingMu sync.Mutex // protects the pending map |
| 35 | + pending map[ID]chan *Response |
| 36 | +} |
51 | 37 |
|
52 |
| - // The result of a request. This member is REQUIRED on success. |
53 |
| - // This member MUST NOT exist if there was an error invoking the method. |
54 |
| - Result *json.RawMessage `json:"result,omitempty"` |
| 38 | +// Handler is an option you can pass to NewConn to handle incoming requests. |
| 39 | +// If the request returns false from IsNotify then the Handler must eventually |
| 40 | +// call Reply on the Conn with the supplied request. |
| 41 | +// Handlers are called synchronously, they should pass the work off to a go |
| 42 | +// routine if they are going to take a long time. |
| 43 | +type Handler func(context.Context, *Conn, *Request) |
| 44 | + |
| 45 | +// Canceler is an option you can pass to NewConn which is invoked for |
| 46 | +// cancelled outgoing requests. |
| 47 | +// The request will have the ID filled in, which can be used to propagate the |
| 48 | +// cancel to the other process if needed. |
| 49 | +// It is okay to use the connection to send notifications, but the context will |
| 50 | +// be in the cancelled state, so you must do it with the background context |
| 51 | +// instead. |
| 52 | +type Canceler func(context.Context, *Conn, *Request) |
| 53 | + |
| 54 | +// NewErrorf builds a Error struct for the suppied message and code. |
| 55 | +// If args is not empty, message and args will be passed to Sprintf. |
| 56 | +func NewErrorf(code ErrorCode, format string, args ...interface{}) *Error { |
| 57 | + return &Error{ |
| 58 | + Code: code, |
| 59 | + Message: fmt.Sprintf(format, args...), |
| 60 | + } |
55 | 61 | }
|
56 | 62 |
|
57 |
| -// ResponseError ... |
58 |
| -type ResponseError struct { |
| 63 | +// NewConn creates a new connection object that reads and writes messages from |
| 64 | +// the supplied stream and dispatches incoming messages to the supplied handler. |
| 65 | +func NewConn(ctx context.Context, s Stream, options ...interface{}) *Conn { |
| 66 | + conn := &Conn{ |
| 67 | + stream: s, |
| 68 | + done: make(chan struct{}), |
| 69 | + pending: make(map[ID]chan *Response), |
| 70 | + } |
| 71 | + for _, opt := range options { |
| 72 | + switch opt := opt.(type) { |
| 73 | + case Handler: |
| 74 | + if conn.handle != nil { |
| 75 | + panic("Duplicate Handler function in options list") |
| 76 | + } |
| 77 | + conn.handle = opt |
| 78 | + case Canceler: |
| 79 | + if conn.cancel != nil { |
| 80 | + panic("Duplicate Canceler function in options list") |
| 81 | + } |
| 82 | + conn.cancel = opt |
| 83 | + default: |
| 84 | + panic(fmt.Errorf("Unknown option type %T in options list", opt)) |
| 85 | + } |
| 86 | + } |
| 87 | + if conn.handle == nil { |
| 88 | + // the default handler reports a method error |
| 89 | + conn.handle = func(ctx context.Context, c *Conn, r *Request) { |
| 90 | + if r.IsNotify() { |
| 91 | + c.Reply(ctx, r, nil, NewErrorf(MethodNotFound, "method %q not found", r.Method)) |
| 92 | + } |
| 93 | + } |
| 94 | + } |
| 95 | + if conn.cancel == nil { |
| 96 | + // the default canceller does nothing |
| 97 | + conn.cancel = func(context.Context, *Conn, *Request) {} |
| 98 | + } |
| 99 | + go func() { |
| 100 | + conn.err = conn.run(ctx) |
| 101 | + close(conn.done) |
| 102 | + }() |
| 103 | + return conn |
| 104 | +} |
59 | 105 |
|
60 |
| - // Code a number indicating the error type that occurred. |
61 |
| - Code ErrorCode `json:"code"` |
| 106 | +func (c *Conn) run(ctx context.Context) error { return nil } |
62 | 107 |
|
63 |
| - // Data a Primitive or Structured value that contains additional |
64 |
| - // information about the error. Can be omitted. |
65 |
| - Data *json.RawMessage `json:"data"` |
| 108 | +func (c *Conn) Call(ctx context.Context, method string, params, result interface{}) error { return nil } |
66 | 109 |
|
67 |
| - // Message a string providing a short description of the error. |
68 |
| - Message string `json:"message"` |
| 110 | +func (c *Conn) Reply(ctx context.Context, req *Request, result interface{}, err error) error { |
| 111 | + return nil |
69 | 112 | }
|
70 | 113 |
|
71 |
| -type NotificationMessage struct { |
72 |
| - Message |
| 114 | +func (c *Conn) Notify(ctx context.Context, method string, params interface{}) error { return nil } |
73 | 115 |
|
74 |
| - // Method is the method to be invoked. |
75 |
| - Method string `json:"method"` |
| 116 | +func (c *Conn) Cancel(id ID) {} |
76 | 117 |
|
77 |
| - // Params is the notification's params. |
78 |
| - Params interface{} `json:"params,omitempty"` |
79 |
| -} |
| 118 | +func (c *Conn) Wait(ctx context.Context) error { return nil } |
0 commit comments