90 lines
2.9 KiB
Go
90 lines
2.9 KiB
Go
|
package jrpc2
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
|
||
|
"github.com/creachadair/jrpc2/code"
|
||
|
)
|
||
|
|
||
|
// Error is the concrete type of errors returned from RPC calls.
|
||
|
type Error struct {
|
||
|
message string
|
||
|
code code.Code
|
||
|
data json.RawMessage
|
||
|
}
|
||
|
|
||
|
// Error renders e to a human-readable string for the error interface.
|
||
|
func (e Error) Error() string { return fmt.Sprintf("[%d] %s", e.code, e.message) }
|
||
|
|
||
|
// Code returns the error code value associated with e.
|
||
|
func (e Error) Code() code.Code { return e.code }
|
||
|
|
||
|
// Message returns the message string associated with e.
|
||
|
func (e Error) Message() string { return e.message }
|
||
|
|
||
|
// HasData reports whether e has error data to unmarshal.
|
||
|
func (e Error) HasData() bool { return len(e.data) != 0 }
|
||
|
|
||
|
// UnmarshalData decodes the error data associated with e into v. It returns
|
||
|
// ErrNoData without modifying v if there was no data message attached to e.
|
||
|
func (e Error) UnmarshalData(v interface{}) error {
|
||
|
if !e.HasData() {
|
||
|
return ErrNoData
|
||
|
}
|
||
|
return json.Unmarshal([]byte(e.data), v)
|
||
|
}
|
||
|
|
||
|
// MarshalJSON implements the json.Marshaler interface for Error values.
|
||
|
func (e Error) MarshalJSON() ([]byte, error) {
|
||
|
return json.Marshal(jerror{C: int32(e.code), M: e.message, D: e.data})
|
||
|
}
|
||
|
|
||
|
// UnmarshalJSON implements the json.Unmarshaler interface for Error values.
|
||
|
func (e *Error) UnmarshalJSON(data []byte) error {
|
||
|
var v jerror
|
||
|
if err := json.Unmarshal(data, &v); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
e.code = code.Code(v.C)
|
||
|
e.message = v.M
|
||
|
e.data = v.D
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// ErrNoData indicates that there are no data to unmarshal.
|
||
|
var ErrNoData = errors.New("no data to unmarshal")
|
||
|
|
||
|
// errServerStopped is returned by Server.Wait when the server was shut down by
|
||
|
// an explicit call to its Stop method or orderly termination of its channel.
|
||
|
var errServerStopped = errors.New("the server has been stopped")
|
||
|
|
||
|
// errClientStopped is the error reported when a client is shut down by an
|
||
|
// explicit call to its Close method.
|
||
|
var errClientStopped = errors.New("the client has been stopped")
|
||
|
|
||
|
// ErrConnClosed is returned by a server's push-to-client methods if they are
|
||
|
// called after the client connection is closed.
|
||
|
var ErrConnClosed = errors.New("client connection is closed")
|
||
|
|
||
|
// Errorf returns an error value of concrete type *Error having the specified
|
||
|
// code and formatted message string.
|
||
|
// It is shorthand for DataErrorf(code, nil, msg, args...)
|
||
|
func Errorf(code code.Code, msg string, args ...interface{}) error {
|
||
|
return DataErrorf(code, nil, msg, args...)
|
||
|
}
|
||
|
|
||
|
// DataErrorf returns an error value of concrete type *Error having the
|
||
|
// specified code, error data, and formatted message string.
|
||
|
// If v == nil this behaves identically to Errorf(code, msg, args...).
|
||
|
func DataErrorf(code code.Code, v interface{}, msg string, args ...interface{}) error {
|
||
|
e := &Error{code: code, message: fmt.Sprintf(msg, args...)}
|
||
|
if v != nil {
|
||
|
if data, err := json.Marshal(v); err == nil {
|
||
|
e.data = data
|
||
|
}
|
||
|
}
|
||
|
return e
|
||
|
}
|