2025-10-15 10:12:44 +03:00

237 lines
4.7 KiB
Go

package lsproto
import (
"errors"
"fmt"
"strconv"
"github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
)
type JSONRPCVersion struct{}
const jsonRPCVersion = `"2.0"`
func (JSONRPCVersion) MarshalJSON() ([]byte, error) {
return []byte(jsonRPCVersion), nil
}
var ErrInvalidJSONRPCVersion = errors.New("invalid JSON-RPC version")
func (*JSONRPCVersion) UnmarshalJSON(data []byte) error {
if string(data) != jsonRPCVersion {
return ErrInvalidJSONRPCVersion
}
return nil
}
type ID struct {
str string
int int32
}
func NewID(rawValue IntegerOrString) *ID {
if rawValue.String != nil {
return &ID{str: *rawValue.String}
}
return &ID{int: *rawValue.Integer}
}
func NewIDString(str string) *ID {
return &ID{str: str}
}
func (id *ID) String() string {
if id.str != "" {
return id.str
}
return strconv.Itoa(int(id.int))
}
func (id *ID) MarshalJSON() ([]byte, error) {
if id.str != "" {
return json.Marshal(id.str)
}
return json.Marshal(id.int)
}
func (id *ID) UnmarshalJSON(data []byte) error {
*id = ID{}
if len(data) > 0 && data[0] == '"' {
return json.Unmarshal(data, &id.str)
}
return json.Unmarshal(data, &id.int)
}
func (id *ID) TryInt() (int32, bool) {
if id == nil || id.str != "" {
return 0, false
}
return id.int, true
}
func (id *ID) MustInt() int32 {
if id.str != "" {
panic("ID is not an integer")
}
return id.int
}
type MessageKind int
const (
MessageKindNotification MessageKind = iota
MessageKindRequest
MessageKindResponse
)
type Message struct {
Kind MessageKind
msg any
}
func (m *Message) AsRequest() *RequestMessage {
return m.msg.(*RequestMessage)
}
func (m *Message) AsResponse() *ResponseMessage {
return m.msg.(*ResponseMessage)
}
func (m *Message) UnmarshalJSON(data []byte) error {
var raw struct {
JSONRPC JSONRPCVersion `json:"jsonrpc"`
Method Method `json:"method"`
ID *ID `json:"id,omitzero"`
Params jsontext.Value `json:"params"`
Result any `json:"result,omitzero"`
Error *ResponseError `json:"error,omitzero"`
}
if err := json.Unmarshal(data, &raw); err != nil {
return fmt.Errorf("%w: %w", ErrInvalidRequest, err)
}
if raw.ID != nil && raw.Method == "" {
m.Kind = MessageKindResponse
m.msg = &ResponseMessage{
JSONRPC: raw.JSONRPC,
ID: raw.ID,
Result: raw.Result,
Error: raw.Error,
}
return nil
}
var params any
var err error
if len(raw.Params) > 0 {
params, err = unmarshalParams(raw.Method, raw.Params)
if err != nil {
return fmt.Errorf("%w: %w", ErrInvalidRequest, err)
}
}
if raw.ID == nil {
m.Kind = MessageKindNotification
} else {
m.Kind = MessageKindRequest
}
m.msg = &RequestMessage{
JSONRPC: raw.JSONRPC,
ID: raw.ID,
Method: raw.Method,
Params: params,
}
return nil
}
func (m *Message) MarshalJSON() ([]byte, error) {
return json.Marshal(m.msg)
}
func NewNotificationMessage(method Method, params any) *RequestMessage {
return &RequestMessage{
JSONRPC: JSONRPCVersion{},
Method: method,
Params: params,
}
}
type RequestMessage struct {
JSONRPC JSONRPCVersion `json:"jsonrpc"`
ID *ID `json:"id,omitzero"`
Method Method `json:"method"`
Params any `json:"params,omitzero"`
}
func NewRequestMessage(method Method, id *ID, params any) *RequestMessage {
return &RequestMessage{
ID: id,
Method: method,
Params: params,
}
}
func (r *RequestMessage) Message() *Message {
return &Message{
Kind: MessageKindRequest,
msg: r,
}
}
func (r *RequestMessage) UnmarshalJSON(data []byte) error {
var raw struct {
JSONRPC JSONRPCVersion `json:"jsonrpc"`
ID *ID `json:"id"`
Method Method `json:"method"`
Params jsontext.Value `json:"params"`
}
if err := json.Unmarshal(data, &raw); err != nil {
return fmt.Errorf("%w: %w", ErrInvalidRequest, err)
}
r.ID = raw.ID
r.Method = raw.Method
var err error
r.Params, err = unmarshalParams(raw.Method, raw.Params)
if err != nil {
return fmt.Errorf("%w: %w", ErrInvalidRequest, err)
}
return nil
}
type ResponseMessage struct {
JSONRPC JSONRPCVersion `json:"jsonrpc"`
ID *ID `json:"id,omitzero"`
Result any `json:"result,omitzero"`
Error *ResponseError `json:"error,omitzero"`
}
func (r *ResponseMessage) Message() *Message {
return &Message{
Kind: MessageKindResponse,
msg: r,
}
}
type ResponseError struct {
Code int32 `json:"code"`
Message string `json:"message"`
Data any `json:"data,omitzero"`
}
func (r *ResponseError) String() string {
if r == nil {
return ""
}
data, err := json.Marshal(r.Data)
if err != nil {
return fmt.Sprintf("[%d]: %s\n%v", r.Code, r.Message, data)
}
return fmt.Sprintf("[%d]: %s", r.Code, r.Message)
}