158 lines
2.9 KiB
Go
158 lines
2.9 KiB
Go
package bridge
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
const ProtocolVersion = 1
|
|
|
|
type MessageType string
|
|
|
|
const (
|
|
Request MessageType = "request"
|
|
Response MessageType = "response"
|
|
Event MessageType = "event"
|
|
)
|
|
|
|
type Message struct {
|
|
Version int `json:"version"`
|
|
Type MessageType `json:"type"`
|
|
RequestId *uint64 `json:"id,omitempty"`
|
|
Name string `json:"name"`
|
|
Body json.RawMessage `json:"body,omitempty"`
|
|
Error *ProtocolError `json:"error,omitempty"`
|
|
}
|
|
|
|
func (m Message) Validate() error {
|
|
if m.Version != ProtocolVersion {
|
|
return fmt.Errorf("unsupported protocol version %d", m.Version)
|
|
}
|
|
|
|
if m.Name == "" {
|
|
return errors.New("message name is required")
|
|
}
|
|
|
|
switch m.Type {
|
|
case Request:
|
|
if m.RequestId == nil {
|
|
return errors.New("request ID is required")
|
|
}
|
|
if m.Error != nil {
|
|
return errors.New("request cannot contain an error")
|
|
}
|
|
case Response:
|
|
if m.RequestId == nil {
|
|
return errors.New("response ID is required")
|
|
}
|
|
if m.Error != nil && len(m.Body) != 0 {
|
|
return errors.New("response cannot contain both body and error")
|
|
}
|
|
|
|
case Event:
|
|
if m.RequestId != nil {
|
|
return errors.New("event cannot contain a request ID")
|
|
}
|
|
if m.Error != nil {
|
|
return errors.New("event cannot contain an error")
|
|
}
|
|
default:
|
|
return fmt.Errorf("unknown message type %q", m.Type)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type ProtocolError struct {
|
|
Code string `json:"code"`
|
|
Message string `json:"message"`
|
|
Data json.RawMessage `json:"data,omitempty"`
|
|
}
|
|
|
|
func (e *ProtocolError) Error() string {
|
|
if e == nil {
|
|
return ""
|
|
}
|
|
|
|
if e.Code == "" {
|
|
return e.Message
|
|
}
|
|
|
|
return fmt.Sprintf("%s: %s", e.Code, e.Message)
|
|
}
|
|
|
|
type SetBreakpointRequest struct {
|
|
File string `json:"file"`
|
|
Line int `json:"line"`
|
|
}
|
|
|
|
type SetBreakpointResponse struct {
|
|
File string `json:"file"`
|
|
Line int `json:"line"`
|
|
Verified bool `json:"verified"`
|
|
}
|
|
|
|
type RemoveBreakpointRequest struct {
|
|
File string `json:"file"`
|
|
Line int `json:"line"`
|
|
}
|
|
|
|
type LocalsResponse struct {
|
|
Variables map[string]string `json:"variables"`
|
|
}
|
|
|
|
type ExecutionStoppedEvent struct {
|
|
Reason string `json:"reason"`
|
|
File string `json:"file"`
|
|
Line int `json:"line"`
|
|
}
|
|
|
|
type ExecutionExitedEvent struct {
|
|
ExitCode int `json:"exit_code"`
|
|
}
|
|
|
|
func newMessage(
|
|
messageType MessageType,
|
|
id *uint64,
|
|
name string,
|
|
body any,
|
|
) (Message, error) {
|
|
var raw json.RawMessage
|
|
|
|
if body != nil {
|
|
encoded, err := json.Marshal(body)
|
|
if err != nil {
|
|
return Message{}, err
|
|
}
|
|
|
|
raw = encoded
|
|
}
|
|
|
|
return Message{
|
|
Version: ProtocolVersion,
|
|
Type: messageType,
|
|
RequestId: id,
|
|
Name: name,
|
|
Body: raw,
|
|
}, nil
|
|
}
|
|
|
|
func newErrorResponse(
|
|
id uint64,
|
|
name string,
|
|
code string,
|
|
message string,
|
|
) Message {
|
|
return Message{
|
|
Version: ProtocolVersion,
|
|
Type: Response,
|
|
RequestId: &id,
|
|
Name: name,
|
|
Error: &ProtocolError{
|
|
Code: code,
|
|
Message: message,
|
|
},
|
|
}
|
|
}
|