brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.0 KiB · 0988f45 Raw
444 lines · cpp
1//===- Protocol.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 "lldb/Protocol/MCP/Protocol.h"10#include "llvm/Support/ErrorHandling.h"11#include "llvm/Support/JSON.h"12 13using namespace llvm;14 15namespace lldb_protocol::mcp {16 17static bool mapRaw(const json::Value &Params, StringLiteral Prop,18                   std::optional<json::Value> &V, json::Path P) {19  const auto *O = Params.getAsObject();20  if (!O) {21    P.report("expected object");22    return false;23  }24  const json::Value *E = O->get(Prop);25  if (E)26    V = std::move(*E);27  return true;28}29 30static llvm::json::Value toJSON(const Id &Id) {31  if (const int64_t *I = std::get_if<int64_t>(&Id))32    return json::Value(*I);33  if (const std::string *S = std::get_if<std::string>(&Id))34    return json::Value(*S);35  llvm_unreachable("unexpected type in protocol::Id");36}37 38static bool mapId(const llvm::json::Value &V, StringLiteral Prop, Id &Id,39                  llvm::json::Path P) {40  const auto *O = V.getAsObject();41  if (!O) {42    P.report("expected object");43    return false;44  }45 46  const auto *E = O->get(Prop);47  if (!E) {48    P.field(Prop).report("not found");49    return false;50  }51 52  if (auto S = E->getAsString()) {53    Id = S->str();54    return true;55  }56 57  if (auto I = E->getAsInteger()) {58    Id = *I;59    return true;60  }61 62  P.report("expected string or number");63  return false;64}65 66llvm::json::Value toJSON(const Request &R) {67  json::Object Result{68      {"jsonrpc", "2.0"}, {"id", toJSON(R.id)}, {"method", R.method}};69  if (R.params)70    Result.insert({"params", R.params});71  return Result;72}73 74bool fromJSON(const llvm::json::Value &V, Request &R, llvm::json::Path P) {75  llvm::json::ObjectMapper O(V, P);76  return O && mapId(V, "id", R.id, P) && O.map("method", R.method) &&77         mapRaw(V, "params", R.params, P);78}79 80bool operator==(const Request &a, const Request &b) {81  return a.id == b.id && a.method == b.method && a.params == b.params;82}83 84llvm::json::Value toJSON(const Error &E) {85  llvm::json::Object Result{{"code", E.code}, {"message", E.message}};86  if (E.data)87    Result.insert({"data", *E.data});88  return Result;89}90 91bool fromJSON(const llvm::json::Value &V, Error &E, llvm::json::Path P) {92  llvm::json::ObjectMapper O(V, P);93  return O && O.map("code", E.code) && O.map("message", E.message) &&94         mapRaw(V, "data", E.data, P);95}96 97bool operator==(const Error &a, const Error &b) {98  return a.code == b.code && a.message == b.message && a.data == b.data;99}100 101llvm::json::Value toJSON(const Response &R) {102  llvm::json::Object Result{{"jsonrpc", "2.0"}, {"id", toJSON(R.id)}};103 104  if (const Error *error = std::get_if<Error>(&R.result))105    Result.insert({"error", *error});106  if (const json::Value *result = std::get_if<json::Value>(&R.result))107    Result.insert({"result", *result});108  return Result;109}110 111bool fromJSON(const llvm::json::Value &V, Response &R, llvm::json::Path P) {112  const json::Object *E = V.getAsObject();113  if (!E) {114    P.report("expected object");115    return false;116  }117 118  const json::Value *result = E->get("result");119  const json::Value *raw_error = E->get("error");120 121  if (result && raw_error) {122    P.report("'result' and 'error' fields are mutually exclusive");123    return false;124  }125 126  if (!result && !raw_error) {127    P.report("'result' or 'error' fields are required'");128    return false;129  }130 131  if (result) {132    R.result = std::move(*result);133  } else {134    Error error;135    if (!fromJSON(*raw_error, error, P))136      return false;137    R.result = std::move(error);138  }139 140  return mapId(V, "id", R.id, P);141}142 143bool operator==(const Response &a, const Response &b) {144  return a.id == b.id && a.result == b.result;145}146 147llvm::json::Value toJSON(const Notification &N) {148  llvm::json::Object Result{{"jsonrpc", "2.0"}, {"method", N.method}};149  if (N.params)150    Result.insert({"params", N.params});151  return Result;152}153 154bool fromJSON(const llvm::json::Value &V, Notification &N, llvm::json::Path P) {155  llvm::json::ObjectMapper O(V, P);156  if (!O || !O.map("method", N.method))157    return false;158  auto *Obj = V.getAsObject();159  if (!Obj)160    return false;161  if (auto *Params = Obj->get("params"))162    N.params = *Params;163  return true;164}165 166bool operator==(const Notification &a, const Notification &b) {167  return a.method == b.method && a.params == b.params;168}169 170bool fromJSON(const llvm::json::Value &V, Resource &R, llvm::json::Path P) {171  llvm::json::ObjectMapper O(V, P);172  return O && O.map("uri", R.uri) && O.map("name", R.name) &&173         O.mapOptional("description", R.description) &&174         O.mapOptional("mimeType", R.mimeType);175}176 177llvm::json::Value toJSON(const Resource &R) {178  llvm::json::Object Result{{"uri", R.uri}, {"name", R.name}};179  if (!R.description.empty())180    Result.insert({"description", R.description});181  if (!R.mimeType.empty())182    Result.insert({"mimeType", R.mimeType});183  return Result;184}185 186llvm::json::Value toJSON(const TextResourceContents &RC) {187  llvm::json::Object Result{{"uri", RC.uri}, {"text", RC.text}};188  if (!RC.mimeType.empty())189    Result.insert({"mimeType", RC.mimeType});190  return Result;191}192 193bool fromJSON(const llvm::json::Value &V, TextResourceContents &RC,194              llvm::json::Path P) {195  llvm::json::ObjectMapper O(V, P);196  return O && O.map("uri", RC.uri) && O.map("text", RC.text) &&197         O.mapOptional("mimeType", RC.mimeType);198}199 200llvm::json::Value toJSON(const ReadResourceResult &RR) {201  return llvm::json::Object{{"contents", RR.contents}};202}203 204bool fromJSON(const llvm::json::Value &V, ReadResourceResult &RR,205              llvm::json::Path P) {206  llvm::json::ObjectMapper O(V, P);207  return O && O.map("contents", RR.contents);208}209 210llvm::json::Value toJSON(const TextContent &TC) {211  return llvm::json::Object{{"type", "text"}, {"text", TC.text}};212}213 214bool fromJSON(const llvm::json::Value &V, TextContent &TC, llvm::json::Path P) {215  llvm::json::ObjectMapper O(V, P);216  return O && O.map("text", TC.text);217}218 219llvm::json::Value toJSON(const ToolDefinition &TD) {220  llvm::json::Object Result{{"name", TD.name}};221  if (!TD.description.empty())222    Result.insert({"description", TD.description});223  if (TD.inputSchema)224    Result.insert({"inputSchema", TD.inputSchema});225  return Result;226}227 228bool fromJSON(const llvm::json::Value &V, ToolDefinition &TD,229              llvm::json::Path P) {230 231  llvm::json::ObjectMapper O(V, P);232  if (!O || !O.map("name", TD.name) ||233      !O.mapOptional("description", TD.description))234    return false;235  return mapRaw(V, "inputSchema", TD.inputSchema, P);236}237 238llvm::json::Value toJSON(const Message &M) {239  return std::visit([](auto &M) { return toJSON(M); }, M);240}241 242bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) {243  const auto *O = V.getAsObject();244  if (!O) {245    P.report("expected object");246    return false;247  }248 249  if (const json::Value *V = O->get("jsonrpc")) {250    if (V->getAsString().value_or("") != "2.0") {251      P.report("unsupported JSON RPC version");252      return false;253    }254  } else {255    P.report("not a valid JSON RPC message");256    return false;257  }258 259  // A message without an ID is a Notification.260  if (!O->get("id")) {261    Notification N;262    if (!fromJSON(V, N, P))263      return false;264    M = std::move(N);265    return true;266  }267 268  if (O->get("method")) {269    Request R;270    if (!fromJSON(V, R, P))271      return false;272    M = std::move(R);273    return true;274  }275 276  if (O->get("result") || O->get("error")) {277    Response R;278    if (!fromJSON(V, R, P))279      return false;280    M = std::move(R);281    return true;282  }283 284  P.report("unrecognized message type");285  return false;286}287 288json::Value toJSON(const Implementation &I) {289  json::Object result{{"name", I.name}, {"version", I.version}};290 291  if (!I.title.empty())292    result.insert({"title", I.title});293 294  return result;295}296 297bool fromJSON(const json::Value &V, Implementation &I, json::Path P) {298  json::ObjectMapper O(V, P);299  return O && O.map("name", I.name) && O.mapOptional("title", I.title) &&300         O.mapOptional("version", I.version);301}302 303json::Value toJSON(const ClientCapabilities &C) { return json::Object{}; }304 305bool fromJSON(const json::Value &, ClientCapabilities &, json::Path) {306  return true;307}308 309json::Value toJSON(const ServerCapabilities &C) {310  json::Object result{};311 312  if (C.supportsToolsList)313    result.insert({"tools", json::Object{{"listChanged", true}}});314 315  if (C.supportsResourcesList || C.supportsResourcesSubscribe) {316    json::Object resources;317    if (C.supportsResourcesList)318      resources.insert({"listChanged", true});319    if (C.supportsResourcesSubscribe)320      resources.insert({"subscribe", true});321    result.insert({"resources", std::move(resources)});322  }323 324  if (C.supportsCompletions)325    result.insert({"completions", json::Object{}});326 327  if (C.supportsLogging)328    result.insert({"logging", json::Object{}});329 330  return result;331}332 333bool fromJSON(const json::Value &V, ServerCapabilities &C, json::Path P) {334  const json::Object *O = V.getAsObject();335  if (!O) {336    P.report("expected object");337    return false;338  }339 340  if (O->find("tools") != O->end())341    C.supportsToolsList = true;342 343  return true;344}345 346json::Value toJSON(const InitializeParams &P) {347  return json::Object{348      {"protocolVersion", P.protocolVersion},349      {"capabilities", P.capabilities},350      {"clientInfo", P.clientInfo},351  };352}353 354bool fromJSON(const json::Value &V, InitializeParams &I, json::Path P) {355  json::ObjectMapper O(V, P);356  return O && O.map("protocolVersion", I.protocolVersion) &&357         O.map("capabilities", I.capabilities) &&358         O.map("clientInfo", I.clientInfo);359}360 361json::Value toJSON(const InitializeResult &R) {362  json::Object result{{"protocolVersion", R.protocolVersion},363                      {"capabilities", R.capabilities},364                      {"serverInfo", R.serverInfo}};365 366  if (!R.instructions.empty())367    result.insert({"instructions", R.instructions});368 369  return result;370}371 372bool fromJSON(const json::Value &V, InitializeResult &R, json::Path P) {373  json::ObjectMapper O(V, P);374  return O && O.map("protocolVersion", R.protocolVersion) &&375         O.map("capabilities", R.capabilities) &&376         O.map("serverInfo", R.serverInfo) &&377         O.mapOptional("instructions", R.instructions);378}379 380json::Value toJSON(const ListToolsResult &R) {381  return json::Object{{"tools", R.tools}};382}383 384bool fromJSON(const json::Value &V, ListToolsResult &R, json::Path P) {385  json::ObjectMapper O(V, P);386  return O && O.map("tools", R.tools);387}388 389json::Value toJSON(const CallToolResult &R) {390  json::Object result{{"content", R.content}};391 392  if (R.isError)393    result.insert({"isError", R.isError});394  if (R.structuredContent)395    result.insert({"structuredContent", *R.structuredContent});396 397  return result;398}399 400bool fromJSON(const json::Value &V, CallToolResult &R, json::Path P) {401  json::ObjectMapper O(V, P);402  return O && O.map("content", R.content) &&403         O.mapOptional("isError", R.isError) &&404         mapRaw(V, "structuredContent", R.structuredContent, P);405}406 407json::Value toJSON(const CallToolParams &R) {408  json::Object result{{"name", R.name}};409 410  if (R.arguments)411    result.insert({"arguments", *R.arguments});412 413  return result;414}415 416bool fromJSON(const json::Value &V, CallToolParams &R, json::Path P) {417  json::ObjectMapper O(V, P);418  return O && O.map("name", R.name) && mapRaw(V, "arguments", R.arguments, P);419}420 421json::Value toJSON(const ReadResourceParams &R) {422  return json::Object{{"uri", R.uri}};423}424 425bool fromJSON(const json::Value &V, ReadResourceParams &R, json::Path P) {426  json::ObjectMapper O(V, P);427  return O && O.map("uri", R.uri);428}429 430json::Value toJSON(const ListResourcesResult &R) {431  return json::Object{{"resources", R.resources}};432}433 434bool fromJSON(const json::Value &V, ListResourcesResult &R, json::Path P) {435  json::ObjectMapper O(V, P);436  return O && O.map("resources", R.resources);437}438 439json::Value toJSON(const Void &R) { return json::Object{}; }440 441bool fromJSON(const json::Value &V, Void &R, json::Path P) { return true; }442 443} // namespace lldb_protocol::mcp444