Files
pybug/internal/bridge/protocol.go
2026-07-13 18:33:39 +02:00

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,
},
}
}