189 lines
6.4 KiB
Go
189 lines
6.4 KiB
Go
// Copyright 2020 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// This file contains utilities for decoding JSON-encoded bytes into DAP message.
|
|
|
|
package dap
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
// DecodeProtocolMessageFieldError describes which JSON attribute
|
|
// has an unsupported value that the decoding cannot handle.
|
|
type DecodeProtocolMessageFieldError struct {
|
|
Seq int
|
|
SubType string
|
|
FieldName string
|
|
FieldValue string
|
|
Message json.RawMessage
|
|
}
|
|
|
|
func (e *DecodeProtocolMessageFieldError) Error() string {
|
|
return fmt.Sprintf("%s %s '%s' is not supported (seq: %d)", e.SubType, e.FieldName, e.FieldValue, e.Seq)
|
|
}
|
|
|
|
// defaultCodec is used to decode vanilla DAP messages.
|
|
var defaultCodec = NewCodec()
|
|
|
|
// Codec is responsible for turning byte blobs into DAP messages.
|
|
type Codec struct {
|
|
eventCtor map[string]messageCtor
|
|
requestCtor map[string]messageCtor
|
|
responseCtor map[string]messageCtor
|
|
}
|
|
|
|
// NewCodec constructs a new codec that extends the vanilla DAP protocol.
|
|
// Unless you need to register custom DAP messages, use
|
|
// DecodeProtocolMessage instead.
|
|
func NewCodec() *Codec {
|
|
ret := &Codec{
|
|
eventCtor: make(map[string]messageCtor),
|
|
requestCtor: make(map[string]messageCtor),
|
|
responseCtor: make(map[string]messageCtor),
|
|
}
|
|
for k, v := range eventCtor {
|
|
ret.eventCtor[k] = v
|
|
}
|
|
for k, v := range requestCtor {
|
|
ret.requestCtor[k] = v
|
|
}
|
|
for k, v := range responseCtor {
|
|
ret.responseCtor[k] = v
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// RegisterRequest registers a new custom DAP command, so that it can be
|
|
// unmarshalled by DecodeMessage. Returns an error when the command already
|
|
// exists.
|
|
//
|
|
// The ctor functions need to return a new instance of the underlying DAP
|
|
// message type. A typical usage looks like this:
|
|
//
|
|
// reqCtor := func() Message { return &LaunchRequest{} }
|
|
// respCtor := func() Message { return &LaunchResponse{} }
|
|
// codec.RegisterRequest("launch", reqCtor, respCtor)
|
|
func (c *Codec) RegisterRequest(command string, requestCtor, responseCtor func() Message) error {
|
|
_, hasReqCtor := c.requestCtor[command]
|
|
_, hasRespCtor := c.responseCtor[command]
|
|
if hasReqCtor || hasRespCtor {
|
|
return fmt.Errorf("command %q is already registered", command)
|
|
}
|
|
c.requestCtor[command] = requestCtor
|
|
c.responseCtor[command] = responseCtor
|
|
return nil
|
|
}
|
|
|
|
// RegisterEvent registers a new custom DAP event, so that it can be
|
|
// unmarshalled by DecodeMessage. Returns an error when the event already
|
|
// exists.
|
|
//
|
|
// The ctor function needs to return a new instance of the underlying DAP
|
|
// message type. A typical usage looks like this:
|
|
//
|
|
// ctor := func() Message { return &StoppedEvent{} }
|
|
// codec.RegisterEvent("stopped", ctor)
|
|
func (c *Codec) RegisterEvent(event string, ctor func() Message) error {
|
|
if _, hasEventCtor := c.eventCtor[event]; hasEventCtor {
|
|
return fmt.Errorf("event %q is already registered", event)
|
|
}
|
|
c.eventCtor[event] = ctor
|
|
return nil
|
|
}
|
|
|
|
// DecodeMessage parses the JSON-encoded data and returns the result of
|
|
// the appropriate type within the ProtocolMessage hierarchy. If message type,
|
|
// command, etc cannot be cast, returns DecodeProtocolMessageFieldError.
|
|
// See also godoc for json.Unmarshal, which is used for underlying decoding.
|
|
func (c *Codec) DecodeMessage(data []byte) (Message, error) {
|
|
// This struct is the union of the ResponseMessage, RequestMessage, and
|
|
// EventMessage types. It is an optimization that saves an additional
|
|
// json.Unmarshal call.
|
|
var m struct {
|
|
ProtocolMessage
|
|
Command string `json:"command"`
|
|
Event string `json:"event"`
|
|
Success bool `json:"success"`
|
|
}
|
|
if err := json.Unmarshal(data, &m); err != nil {
|
|
return nil, err
|
|
}
|
|
switch m.Type {
|
|
case "request":
|
|
return c.decodeRequest(m.Command, m.Seq, data)
|
|
case "response":
|
|
return c.decodeResponse(m.Command, m.Seq, m.Success, data)
|
|
case "event":
|
|
return c.decodeEvent(m.Event, m.Seq, data)
|
|
default:
|
|
return nil, &DecodeProtocolMessageFieldError{m.Seq, "ProtocolMessage", "type", m.Type, json.RawMessage(data)}
|
|
}
|
|
}
|
|
|
|
// decodeRequest determines what request type in the ProtocolMessage hierarchy
|
|
// data corresponds to and uses json.Unmarshal to populate the corresponding
|
|
// struct to be returned.
|
|
func (c *Codec) decodeRequest(command string, seq int, data []byte) (Message, error) {
|
|
ctor, ok := c.requestCtor[command]
|
|
if !ok {
|
|
return nil, &DecodeProtocolMessageFieldError{seq, "Request", "command", command, json.RawMessage(data)}
|
|
}
|
|
requestPtr := ctor()
|
|
err := json.Unmarshal(data, requestPtr)
|
|
return requestPtr, err
|
|
}
|
|
|
|
// decodeResponse determines what response type in the ProtocolMessage hierarchy
|
|
// data corresponds to and uses json.Unmarshal to populate the corresponding
|
|
// struct to be returned.
|
|
func (c *Codec) decodeResponse(command string, seq int, success bool, data []byte) (Message, error) {
|
|
if !success {
|
|
var er ErrorResponse
|
|
err := json.Unmarshal(data, &er)
|
|
return &er, err
|
|
}
|
|
ctor, ok := c.responseCtor[command]
|
|
if !ok {
|
|
return nil, &DecodeProtocolMessageFieldError{seq, "Response", "command", command, json.RawMessage(data)}
|
|
}
|
|
responsePtr := ctor()
|
|
err := json.Unmarshal(data, responsePtr)
|
|
return responsePtr, err
|
|
}
|
|
|
|
// decodeEvent determines what event type in the ProtocolMessage hierarchy
|
|
// data corresponds to and uses json.Unmarshal to populate the corresponding
|
|
// struct to be returned.
|
|
func (c *Codec) decodeEvent(event string, seq int, data []byte) (Message, error) {
|
|
ctor, ok := c.eventCtor[event]
|
|
if !ok {
|
|
return nil, &DecodeProtocolMessageFieldError{seq, "Event", "event", event, json.RawMessage(data)}
|
|
}
|
|
eventPtr := ctor()
|
|
err := json.Unmarshal(data, eventPtr)
|
|
return eventPtr, err
|
|
}
|
|
|
|
// DecodeProtocolMessage parses the JSON-encoded ProtocolMessage and returns
|
|
// the message embedded in it. If message type, command, etc cannot be cast,
|
|
// returns DecodeProtocolMessageFieldError. See also godoc for json.Unmarshal,
|
|
// which is used for underlying decoding.
|
|
func DecodeProtocolMessage(data []byte) (Message, error) {
|
|
return defaultCodec.DecodeMessage(data)
|
|
}
|
|
|
|
type messageCtor func() Message
|