package jsonrpc import ( "encoding/json" "sync" ) // Request is a request the peer sent, waiting for an answer. // // It exists so that an answer can be given later than the moment the request // arrived. A language server's questions can all be answered on the spot; an // agent asking permission to run a command cannot be, because the answer comes // from a dialog somebody has to look at, and opening one belongs to the // goroutine that draws. // // func (a *App) onRequest(req *jsonrpc.Request) { // if req.Method != "session/request_permission" { // req.Reply(nil, &jsonrpc.ResponseError{Code: jsonrpc.CodeMethodNotFound, Message: req.Method}) // return // } // a.pendingPermission = req // the event loop opens the dialog and replies // } type Request struct { // Method is what the peer asked for. Method string // Params are its parameters, still encoded. Decode them into whatever the // method's parameters are. Params json.RawMessage conn *Conn id json.RawMessage once sync.Once } // Reply answers the request, with result when err is nil and with err // otherwise. // // It may be called from any goroutine and at any time after the request // arrived. Only the first call has any effect: a permission dialog that is // answered and then torn down by the window closing would otherwise write two // responses carrying one id. // // A failure to write the answer is not reported. The only reason it can fail // is that the peer has gone, which the read loop sees for itself, and there is // nothing a caller could usefully do about it. // // req.Reply(map[string]any{"outcome": outcome}, nil) // req.Reply(nil, errors.New("no such file")) func (r *Request) Reply(result any, err error) { r.once.Do(func() { r.reply(result, err) }) } // reply encodes one answer and writes it. func (r *Request) reply(result any, err error) { answer := message{JSONRPC: Version, ID: r.id} switch { case err != nil: answer.Error = asResponseError(err) default: encoded, marshalErr := json.Marshal(result) if marshalErr != nil { answer.Error = &ResponseError{Code: CodeInternalError, Message: marshalErr.Error()} } else { answer.Result = encoded } } _ = r.conn.write(&answer) }