307 lines · cpp
1//===-- ProtocolBase.cpp --------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "Protocol/ProtocolBase.h"10#include "llvm/ADT/StringRef.h"11#include "llvm/ADT/StringSwitch.h"12#include "llvm/Support/ErrorHandling.h"13#include "llvm/Support/JSON.h"14#include <optional>15#include <utility>16 17using namespace llvm;18 19static bool mapRaw(const json::Value &Params, StringLiteral Prop,20 std::optional<json::Value> &V, json::Path P) {21 const auto *O = Params.getAsObject();22 if (!O) {23 P.report("expected object");24 return false;25 }26 const json::Value *E = O->get(Prop);27 if (E)28 V = std::move(*E);29 return true;30}31 32namespace lldb_dap::protocol {33 34enum MessageType : unsigned {35 eMessageTypeRequest,36 eMessageTypeResponse,37 eMessageTypeEvent38};39 40bool fromJSON(const json::Value &Params, MessageType &M, json::Path P) {41 auto rawType = Params.getAsString();42 if (!rawType) {43 P.report("expected a string");44 return false;45 }46 std::optional<MessageType> type =47 StringSwitch<std::optional<MessageType>>(*rawType)48 .Case("request", eMessageTypeRequest)49 .Case("response", eMessageTypeResponse)50 .Case("event", eMessageTypeEvent)51 .Default(std::nullopt);52 if (!type) {53 P.report("unexpected value, expected 'request', 'response' or 'event'");54 return false;55 }56 M = *type;57 return true;58}59 60json::Value toJSON(const Request &R) {61 assert(R.seq != kCalculateSeq && "invalid seq");62 63 json::Object Result{64 {"type", "request"},65 {"seq", R.seq},66 {"command", R.command},67 };68 69 if (R.arguments)70 Result.insert({"arguments", R.arguments});71 72 return std::move(Result);73}74 75bool fromJSON(json::Value const &Params, Request &R, json::Path P) {76 json::ObjectMapper O(Params, P);77 if (!O)78 return false;79 80 MessageType type;81 if (!O.map("type", type) || !O.map("command", R.command) ||82 !O.map("seq", R.seq))83 return false;84 85 if (type != eMessageTypeRequest) {86 P.field("type").report("expected to be 'request'");87 return false;88 }89 90 if (R.command.empty()) {91 P.field("command").report("expected to not be ''");92 return false;93 }94 95 if (!R.seq) {96 P.field("seq").report("expected to not be '0'");97 return false;98 }99 100 return mapRaw(Params, "arguments", R.arguments, P);101}102 103bool operator==(const Request &a, const Request &b) {104 return a.seq == b.seq && a.command == b.command && a.arguments == b.arguments;105}106 107json::Value toJSON(const Response &R) {108 assert(R.seq != kCalculateSeq && "invalid seq");109 110 json::Object Result{{"type", "response"},111 {"seq", R.seq},112 {"command", R.command},113 {"request_seq", R.request_seq},114 {"success", R.success}};115 116 if (R.message) {117 assert(!R.success && "message can only be used if success is false");118 if (const auto *messageEnum = std::get_if<ResponseMessage>(&*R.message)) {119 switch (*messageEnum) {120 case eResponseMessageCancelled:121 Result.insert({"message", "cancelled"});122 break;123 case eResponseMessageNotStopped:124 Result.insert({"message", "notStopped"});125 break;126 }127 } else if (const auto *messageString =128 std::get_if<std::string>(&*R.message)) {129 Result.insert({"message", *messageString});130 }131 }132 133 if (R.body)134 Result.insert({"body", R.body});135 136 return std::move(Result);137}138 139static bool fromJSON(json::Value const &Params,140 std::variant<ResponseMessage, std::string> &M,141 json::Path P) {142 auto rawMessage = Params.getAsString();143 if (!rawMessage) {144 P.report("expected a string");145 return false;146 }147 std::optional<ResponseMessage> message =148 StringSwitch<std::optional<ResponseMessage>>(*rawMessage)149 .Case("cancelled", eResponseMessageCancelled)150 .Case("notStopped", eResponseMessageNotStopped)151 .Default(std::nullopt);152 if (message)153 M = *message;154 else if (!rawMessage->empty())155 M = rawMessage->str();156 return true;157}158 159bool fromJSON(json::Value const &Params, Response &R, json::Path P) {160 json::ObjectMapper O(Params, P);161 if (!O)162 return false;163 164 MessageType type;165 if (!O.map("type", type) || !O.map("seq", R.seq) ||166 !O.map("command", R.command) || !O.map("request_seq", R.request_seq))167 return false;168 169 if (type != eMessageTypeResponse) {170 P.field("type").report("expected to be 'response'");171 return false;172 }173 174 if (R.command.empty()) {175 P.field("command").report("expected to not be empty");176 return false;177 }178 179 return O.map("success", R.success) && O.map("message", R.message) &&180 mapRaw(Params, "body", R.body, P);181}182 183bool operator==(const Response &a, const Response &b) {184 return a.request_seq == b.request_seq && a.command == b.command &&185 a.success == b.success && a.message == b.message && a.body == b.body;186}187 188json::Value toJSON(const ErrorMessage &EM) {189 json::Object Result{{"id", EM.id}, {"format", EM.format}};190 191 if (EM.variables) {192 json::Object variables;193 for (auto &var : *EM.variables)194 variables[var.first] = var.second;195 Result.insert({"variables", std::move(variables)});196 }197 if (EM.sendTelemetry)198 Result.insert({"sendTelemetry", EM.sendTelemetry});199 if (EM.showUser)200 Result.insert({"showUser", EM.showUser});201 if (EM.url)202 Result.insert({"url", EM.url});203 if (EM.urlLabel)204 Result.insert({"urlLabel", EM.urlLabel});205 206 return std::move(Result);207}208 209bool fromJSON(json::Value const &Params, ErrorMessage &EM, json::Path P) {210 json::ObjectMapper O(Params, P);211 return O && O.map("id", EM.id) && O.map("format", EM.format) &&212 O.map("variables", EM.variables) &&213 O.map("sendTelemetry", EM.sendTelemetry) &&214 O.map("showUser", EM.showUser) && O.map("url", EM.url) &&215 O.map("urlLabel", EM.urlLabel);216}217 218json::Value toJSON(const Event &E) {219 assert(E.seq != kCalculateSeq && "invalid seq");220 221 json::Object Result{222 {"type", "event"},223 {"seq", E.seq},224 {"event", E.event},225 };226 227 if (E.body)228 Result.insert({"body", E.body});229 230 return std::move(Result);231}232 233bool fromJSON(json::Value const &Params, Event &E, json::Path P) {234 json::ObjectMapper O(Params, P);235 if (!O)236 return false;237 238 MessageType type;239 if (!O.map("type", type) || !O.map("seq", E.seq) || !O.map("event", E.event))240 return false;241 242 if (type != eMessageTypeEvent) {243 P.field("type").report("expected to be 'event'");244 return false;245 }246 247 if (E.event.empty()) {248 P.field("event").report("expected to not be empty");249 return false;250 }251 252 return mapRaw(Params, "body", E.body, P);253}254 255bool operator==(const Event &a, const Event &b) {256 return a.event == b.event && a.body == b.body;257}258 259bool fromJSON(const json::Value &Params, Message &PM, json::Path P) {260 json::ObjectMapper O(Params, P);261 if (!O)262 return false;263 264 MessageType type;265 if (!O.map("type", type))266 return false;267 268 switch (type) {269 case eMessageTypeRequest: {270 Request req;271 if (!fromJSON(Params, req, P))272 return false;273 PM = std::move(req);274 return true;275 }276 case eMessageTypeResponse: {277 Response resp;278 if (!fromJSON(Params, resp, P))279 return false;280 PM = std::move(resp);281 return true;282 }283 case eMessageTypeEvent:284 Event evt;285 if (!fromJSON(Params, evt, P))286 return false;287 PM = std::move(evt);288 return true;289 }290 llvm_unreachable("unhandled message type request.");291}292 293json::Value toJSON(const Message &M) {294 return std::visit([](auto &M) { return toJSON(M); }, M);295}296 297json::Value toJSON(const ErrorResponseBody &E) {298 json::Object result{};299 300 if (E.error)301 result.insert({"error", *E.error});302 303 return result;304}305 306} // namespace lldb_dap::protocol307