90 lines
2.3 KiB
Go
90 lines
2.3 KiB
Go
|
|
package jsonrpc
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"reflect"
|
||
|
|
)
|
||
|
|
|
||
|
|
type response struct {
|
||
|
|
Jsonrpc string `json:"jsonrpc"`
|
||
|
|
Result interface{} `json:"result,omitempty"`
|
||
|
|
ID interface{} `json:"id"`
|
||
|
|
Error *JSONRPCError `json:"error,omitempty"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r response) MarshalJSON() ([]byte, error) {
|
||
|
|
// Custom marshal logic as per JSON-RPC 2.0 spec:
|
||
|
|
// > `result`:
|
||
|
|
// > This member is REQUIRED on success.
|
||
|
|
// > This member MUST NOT exist if there was an error invoking the method.
|
||
|
|
//
|
||
|
|
// > `error`:
|
||
|
|
// > This member is REQUIRED on error.
|
||
|
|
// > This member MUST NOT exist if there was no error triggered during invocation.
|
||
|
|
data := map[string]interface{}{
|
||
|
|
"jsonrpc": r.Jsonrpc,
|
||
|
|
"id": r.ID,
|
||
|
|
}
|
||
|
|
|
||
|
|
if r.Error != nil {
|
||
|
|
data["error"] = r.Error
|
||
|
|
} else {
|
||
|
|
data["result"] = r.Result
|
||
|
|
}
|
||
|
|
return json.Marshal(data)
|
||
|
|
}
|
||
|
|
|
||
|
|
type JSONRPCError struct {
|
||
|
|
Code ErrorCode `json:"code"`
|
||
|
|
Message string `json:"message"`
|
||
|
|
Meta json.RawMessage `json:"meta,omitempty"`
|
||
|
|
Data interface{} `json:"data,omitempty"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *JSONRPCError) Error() string {
|
||
|
|
if e.Code >= -32768 && e.Code <= -32000 {
|
||
|
|
return fmt.Sprintf("RPC error (%d): %s", e.Code, e.Message)
|
||
|
|
}
|
||
|
|
return e.Message
|
||
|
|
}
|
||
|
|
|
||
|
|
var (
|
||
|
|
_ error = (*JSONRPCError)(nil)
|
||
|
|
marshalableRT = reflect.TypeOf(new(marshalable)).Elem()
|
||
|
|
errorCodecRT = reflect.TypeOf(new(RPCErrorCodec)).Elem()
|
||
|
|
)
|
||
|
|
|
||
|
|
func (e *JSONRPCError) val(errors *Errors) reflect.Value {
|
||
|
|
if errors != nil {
|
||
|
|
t, ok := errors.byCode[e.Code]
|
||
|
|
if ok {
|
||
|
|
var v reflect.Value
|
||
|
|
if t.Kind() == reflect.Ptr {
|
||
|
|
v = reflect.New(t.Elem())
|
||
|
|
} else {
|
||
|
|
v = reflect.New(t)
|
||
|
|
}
|
||
|
|
|
||
|
|
if v.Type().Implements(errorCodecRT) {
|
||
|
|
if err := v.Interface().(RPCErrorCodec).FromJSONRPCError(*e); err != nil {
|
||
|
|
log.Errorf("Error converting JSONRPCError to custom error type '%s' (code %d): %w", t.String(), e.Code, err)
|
||
|
|
return reflect.ValueOf(e)
|
||
|
|
}
|
||
|
|
} else if len(e.Meta) > 0 && v.Type().Implements(marshalableRT) {
|
||
|
|
if err := v.Interface().(marshalable).UnmarshalJSON(e.Meta); err != nil {
|
||
|
|
log.Errorf("Error unmarshalling error metadata to custom error type '%s' (code %d): %w", t.String(), e.Code, err)
|
||
|
|
return reflect.ValueOf(e)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if t.Kind() != reflect.Ptr {
|
||
|
|
v = v.Elem()
|
||
|
|
}
|
||
|
|
return v
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return reflect.ValueOf(e)
|
||
|
|
}
|