wip, rework bridge
This commit is contained in:
@@ -2,43 +2,156 @@ package bridge
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"maps"
|
||||
"math/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type CommandType string
|
||||
const ProtocolVersion = 1
|
||||
|
||||
type MessageType string
|
||||
|
||||
const (
|
||||
ContinueCommand CommandType = "continue"
|
||||
BreakCommand CommandType = "break"
|
||||
UnbreakCommand CommandType = "unbreak"
|
||||
LocalsCommand CommandType = "locals"
|
||||
StepCommand CommandType = "step"
|
||||
Request MessageType = "request"
|
||||
Response MessageType = "response"
|
||||
Event MessageType = "event"
|
||||
)
|
||||
|
||||
func makeCommand(cmd CommandType, values map[string]any) (requestId string, request string) {
|
||||
m := make(map[string]any, len(values)+2)
|
||||
maps.Copy(m, values)
|
||||
|
||||
requestId = randString(8)
|
||||
|
||||
m["cmd"] = cmd
|
||||
m["request_id"] = requestId
|
||||
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
panic("failed to marshal command " + err.Error())
|
||||
}
|
||||
|
||||
return requestId, string(b)
|
||||
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"`
|
||||
}
|
||||
|
||||
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
func randString(n int) string {
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
||||
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,
|
||||
},
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user