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