annotate utilities/vampipe-convert.cpp @ 74:d45cfa25aaad

Ensure code is at least set, but just to 0 at the mo
author Chris Cannam <c.cannam@qmul.ac.uk>
date Mon, 10 Oct 2016 15:31:44 +0100
parents 7bfc07576830
children
rev   line source
c@23 1
c@23 2 #include "VampJson.h"
c@23 3 #include "VampnProto.h"
c@23 4
c@31 5 #include "bits/RequestOrResponse.h"
c@32 6 #include "bits/PreservingPluginHandleMapper.h"
c@31 7
c@23 8 #include <iostream>
c@23 9 #include <sstream>
c@23 10 #include <stdexcept>
c@23 11
c@23 12 using namespace std;
c@23 13 using namespace json11;
c@23 14 using namespace vampipe;
c@23 15
c@23 16 void usage()
c@23 17 {
c@23 18 string myname = "vampipe-convert";
c@23 19 cerr << "\n" << myname <<
c@24 20 ": Validate and convert Vamp request and response messages\n\n"
c@24 21 " Usage: " << myname << " [-i <informat>] [-o <outformat>] request\n"
c@24 22 " " << myname << " [-i <informat>] [-o <outformat>] response\n\n"
c@24 23 " where\n"
c@24 24 " <informat>: the format to read from stdin\n"
c@24 25 " (\"json\" or \"capnp\", default is \"json\")\n"
c@24 26 " <outformat>: the format to convert to and write to stdout\n"
c@44 27 " (\"json\", \"json-b64\" or \"capnp\", default is \"json\")\n"
c@44 28 " request|response: whether messages are Vamp request or response type\n\n"
c@24 29 "If <informat> and <outformat> differ, convert from <informat> to <outformat>.\n"
c@24 30 "If <informat> and <outformat> are the same, just check validity of incoming\n"
c@44 31 "messages and pass them to output.\n\n"
c@44 32 "Specifying \"json-b64\" as output format forces base64 encoding for process and\n"
c@44 33 "feature blocks, unlike the \"json\" output format which uses text encoding.\n"
c@44 34 "The \"json\" input format accepts either.\n\n";
c@24 35
c@23 36 exit(2);
c@23 37 }
c@23 38
c@24 39 Json
c@60 40 convertRequestJson(string input, string &err)
c@24 41 {
c@24 42 Json j = Json::parse(input, err);
c@24 43 if (err != "") {
c@60 44 err = "invalid json: " + err;
c@60 45 return {};
c@24 46 }
c@24 47 if (!j.is_object()) {
c@60 48 err = "object expected at top level";
c@67 49 } else if (!j["method"].is_string()) {
c@67 50 err = "string expected for method field";
c@67 51 } else if (!j["params"].is_null() && !j["params"].is_object()) {
c@67 52 err = "object expected for params field";
c@24 53 }
c@24 54 return j;
c@24 55 }
c@24 56
c@24 57 Json
c@60 58 convertResponseJson(string input, string &err)
c@24 59 {
c@24 60 Json j = Json::parse(input, err);
c@24 61 if (err != "") {
c@60 62 err = "invalid json: " + err;
c@60 63 return {};
c@24 64 }
c@24 65 if (!j.is_object()) {
c@60 66 err = "object expected at top level";
c@67 67 } else {
c@67 68 if (!j["result"].is_object()) {
c@67 69 if (!j["error"].is_object()) {
c@67 70 err = "expected either result or error object";
c@67 71 }
c@67 72 }
c@24 73 }
c@24 74 return j;
c@24 75 }
c@24 76
c@30 77 //!!! Lots of potential for refactoring the conversion classes based
c@30 78 //!!! on the common matter in the following eight functions...
c@30 79
c@32 80 PreservingPluginHandleMapper mapper;
c@32 81
c@73 82 static RequestOrResponse::RpcId
c@73 83 readJsonId(const Json &j)
c@73 84 {
c@73 85 RequestOrResponse::RpcId id;
c@73 86
c@73 87 if (j["id"].is_number()) {
c@73 88 id.type = RequestOrResponse::RpcId::Number;
c@73 89 id.number = j["id"].number_value();
c@73 90 } else if (j["id"].is_string()) {
c@73 91 id.type = RequestOrResponse::RpcId::Tag;
c@73 92 id.tag = j["id"].string_value();
c@73 93 } else {
c@73 94 id.type = RequestOrResponse::RpcId::Absent;
c@73 95 }
c@73 96
c@73 97 return id;
c@73 98 }
c@73 99
c@73 100 static Json
c@73 101 writeJsonId(const RequestOrResponse::RpcId &id)
c@73 102 {
c@73 103 if (id.type == RequestOrResponse::RpcId::Number) {
c@73 104 return id.number;
c@73 105 } else if (id.type == RequestOrResponse::RpcId::Tag) {
c@73 106 return id.tag;
c@73 107 } else {
c@73 108 return Json();
c@73 109 }
c@73 110 }
c@73 111
c@73 112 template <typename Reader>
c@73 113 static RequestOrResponse::RpcId
c@73 114 readCapnpId(const Reader &r)
c@73 115 {
c@73 116 int number;
c@73 117 string tag;
c@73 118 switch (r.getId().which()) {
c@73 119 case RpcRequest::Id::Which::NUMBER:
c@73 120 number = r.getId().getNumber();
c@73 121 return { RequestOrResponse::RpcId::Number, number, "" };
c@73 122 case RpcRequest::Id::Which::TAG:
c@73 123 tag = r.getId().getTag();
c@73 124 return { RequestOrResponse::RpcId::Tag, 0, tag };
c@73 125 case RpcRequest::Id::Which::NONE:
c@73 126 return { RequestOrResponse::RpcId::Absent, 0, "" };
c@73 127 }
c@73 128 return {};
c@73 129 }
c@73 130
c@73 131 template <typename Builder>
c@73 132 static void
c@73 133 buildCapnpId(Builder &b, const RequestOrResponse::RpcId &id)
c@73 134 {
c@73 135 switch (id.type) {
c@73 136 case RequestOrResponse::RpcId::Number:
c@73 137 b.getId().setNumber(id.number);
c@73 138 break;
c@73 139 case RequestOrResponse::RpcId::Tag:
c@73 140 b.getId().setTag(id.tag);
c@73 141 break;
c@73 142 case RequestOrResponse::RpcId::Absent:
c@73 143 b.getId().setNone();
c@73 144 break;
c@73 145 }
c@73 146 }
c@73 147
c@23 148 RequestOrResponse
c@60 149 readRequestJson(string &err)
c@23 150 {
c@23 151 RequestOrResponse rr;
c@24 152 rr.direction = RequestOrResponse::Request;
c@24 153
c@24 154 string input;
c@24 155 if (!getline(cin, input)) {
c@60 156 // the EOF case, not actually an error
c@25 157 rr.type = RRType::NotValid;
c@24 158 return rr;
c@24 159 }
c@24 160
c@60 161 Json j = convertRequestJson(input, err);
c@60 162 if (err != "") return {};
c@27 163
c@60 164 rr.type = VampJson::getRequestResponseType(j, err);
c@60 165 if (err != "") return {};
c@73 166
c@73 167 rr.id = readJsonId(j);
c@73 168
c@66 169 VampJson::BufferSerialisation serialisation =
c@66 170 VampJson::BufferSerialisation::Array;
c@24 171
c@27 172 switch (rr.type) {
c@27 173
c@27 174 case RRType::List:
c@68 175 VampJson::toRpcRequest_List(j, err); // type check only
c@27 176 break;
c@27 177 case RRType::Load:
c@68 178 rr.loadRequest = VampJson::toRpcRequest_Load(j, err);
c@27 179 break;
c@27 180 case RRType::Configure:
c@68 181 rr.configurationRequest = VampJson::toRpcRequest_Configure(j, mapper, err);
c@27 182 break;
c@27 183 case RRType::Process:
c@68 184 rr.processRequest = VampJson::toRpcRequest_Process(j, mapper, serialisation, err);
c@27 185 break;
c@27 186 case RRType::Finish:
c@68 187 rr.finishRequest = VampJson::toRpcRequest_Finish(j, mapper, err);
c@27 188 break;
c@27 189 case RRType::NotValid:
c@27 190 break;
c@24 191 }
c@24 192
c@24 193 return rr;
c@24 194 }
c@24 195
c@24 196 void
c@44 197 writeRequestJson(RequestOrResponse &rr, bool useBase64)
c@24 198 {
c@24 199 Json j;
c@24 200
c@54 201 VampJson::BufferSerialisation serialisation =
c@54 202 (useBase64 ?
c@54 203 VampJson::BufferSerialisation::Base64 :
c@66 204 VampJson::BufferSerialisation::Array);
c@54 205
c@73 206 Json id = writeJsonId(rr.id);
c@73 207
c@27 208 switch (rr.type) {
c@27 209
c@27 210 case RRType::List:
c@73 211 j = VampJson::fromRpcRequest_List(id);
c@27 212 break;
c@27 213 case RRType::Load:
c@73 214 j = VampJson::fromRpcRequest_Load(rr.loadRequest, id);
c@27 215 break;
c@27 216 case RRType::Configure:
c@73 217 j = VampJson::fromRpcRequest_Configure(rr.configurationRequest, mapper, id);
c@27 218 break;
c@27 219 case RRType::Process:
c@68 220 j = VampJson::fromRpcRequest_Process
c@73 221 (rr.processRequest, mapper, serialisation, id);
c@27 222 break;
c@27 223 case RRType::Finish:
c@73 224 j = VampJson::fromRpcRequest_Finish(rr.finishRequest, mapper, id);
c@27 225 break;
c@27 226 case RRType::NotValid:
c@27 227 break;
c@24 228 }
c@24 229
c@24 230 cout << j.dump() << endl;
c@24 231 }
c@24 232
c@24 233 RequestOrResponse
c@60 234 readResponseJson(string &err)
c@24 235 {
c@24 236 RequestOrResponse rr;
c@24 237 rr.direction = RequestOrResponse::Response;
c@24 238
c@23 239 string input;
c@23 240 if (!getline(cin, input)) {
c@60 241 // the EOF case, not actually an error
c@25 242 rr.type = RRType::NotValid;
c@23 243 return rr;
c@23 244 }
c@23 245
c@60 246 Json j = convertResponseJson(input, err);
c@60 247 if (err != "") return {};
c@27 248
c@60 249 rr.type = VampJson::getRequestResponseType(j, err);
c@60 250 if (err != "") return {};
c@73 251
c@73 252 rr.id = readJsonId(j);
c@60 253
c@66 254 VampJson::BufferSerialisation serialisation =
c@66 255 VampJson::BufferSerialisation::Array;
c@23 256
c@52 257 rr.success = j["success"].bool_value();
c@52 258 rr.errorText = j["errorText"].string_value();
c@52 259
c@27 260 switch (rr.type) {
c@27 261
c@27 262 case RRType::List:
c@68 263 rr.listResponse = VampJson::toRpcResponse_List(j, err);
c@27 264 break;
c@27 265 case RRType::Load:
c@68 266 rr.loadResponse = VampJson::toRpcResponse_Load(j, mapper, err);
c@27 267 break;
c@27 268 case RRType::Configure:
c@68 269 rr.configurationResponse = VampJson::toRpcResponse_Configure(j, mapper, err);
c@27 270 break;
c@27 271 case RRType::Process:
c@68 272 rr.processResponse = VampJson::toRpcResponse_Process(j, mapper, serialisation, err);
c@27 273 break;
c@27 274 case RRType::Finish:
c@68 275 rr.finishResponse = VampJson::toRpcResponse_Finish(j, mapper, serialisation, err);
c@27 276 break;
c@27 277 case RRType::NotValid:
c@27 278 break;
c@23 279 }
c@23 280
c@23 281 return rr;
c@23 282 }
c@23 283
c@24 284 void
c@44 285 writeResponseJson(RequestOrResponse &rr, bool useBase64)
c@24 286 {
c@24 287 Json j;
c@24 288
c@53 289 VampJson::BufferSerialisation serialisation =
c@54 290 (useBase64 ?
c@53 291 VampJson::BufferSerialisation::Base64 :
c@66 292 VampJson::BufferSerialisation::Array);
c@53 293
c@73 294 Json id = writeJsonId(rr.id);
c@73 295
c@52 296 if (!rr.success) {
c@27 297
c@73 298 j = VampJson::fromError(rr.errorText, rr.type, id);
c@52 299
c@52 300 } else {
c@52 301
c@52 302 switch (rr.type) {
c@52 303
c@52 304 case RRType::List:
c@73 305 j = VampJson::fromRpcResponse_List(rr.listResponse, id);
c@52 306 break;
c@52 307 case RRType::Load:
c@73 308 j = VampJson::fromRpcResponse_Load(rr.loadResponse, mapper, id);
c@52 309 break;
c@52 310 case RRType::Configure:
c@68 311 j = VampJson::fromRpcResponse_Configure(rr.configurationResponse,
c@73 312 mapper, id);
c@52 313 break;
c@52 314 case RRType::Process:
c@68 315 j = VampJson::fromRpcResponse_Process
c@73 316 (rr.processResponse, mapper, serialisation, id);
c@52 317 break;
c@52 318 case RRType::Finish:
c@68 319 j = VampJson::fromRpcResponse_Finish
c@73 320 (rr.finishResponse, mapper, serialisation, id);
c@52 321 break;
c@52 322 case RRType::NotValid:
c@52 323 break;
c@52 324 }
c@24 325 }
c@52 326
c@27 327 cout << j.dump() << endl;
c@27 328 }
c@25 329
c@27 330 RequestOrResponse
c@33 331 readRequestCapnp(kj::BufferedInputStreamWrapper &buffered)
c@27 332 {
c@27 333 RequestOrResponse rr;
c@27 334 rr.direction = RequestOrResponse::Request;
c@27 335
c@33 336 ::capnp::InputStreamMessageReader message(buffered);
c@68 337 RpcRequest::Reader reader = message.getRoot<RpcRequest>();
c@27 338
c@27 339 rr.type = VampnProto::getRequestResponseType(reader);
c@73 340 rr.id = readCapnpId(reader);
c@27 341
c@27 342 switch (rr.type) {
c@27 343
c@27 344 case RRType::List:
c@68 345 VampnProto::readRpcRequest_List(reader); // type check only
c@27 346 break;
c@27 347 case RRType::Load:
c@68 348 VampnProto::readRpcRequest_Load(rr.loadRequest, reader);
c@27 349 break;
c@27 350 case RRType::Configure:
c@68 351 VampnProto::readRpcRequest_Configure(rr.configurationRequest,
c@32 352 reader, mapper);
c@27 353 break;
c@27 354 case RRType::Process:
c@68 355 VampnProto::readRpcRequest_Process(rr.processRequest, reader, mapper);
c@27 356 break;
c@27 357 case RRType::Finish:
c@68 358 VampnProto::readRpcRequest_Finish(rr.finishRequest, reader, mapper);
c@27 359 break;
c@27 360 case RRType::NotValid:
c@27 361 break;
c@27 362 }
c@27 363
c@27 364 return rr;
c@27 365 }
c@27 366
c@29 367 void
c@29 368 writeRequestCapnp(RequestOrResponse &rr)
c@29 369 {
c@29 370 ::capnp::MallocMessageBuilder message;
c@68 371 RpcRequest::Builder builder = message.initRoot<RpcRequest>();
c@29 372
c@73 373 buildCapnpId(builder, rr.id);
c@73 374
c@29 375 switch (rr.type) {
c@29 376
c@29 377 case RRType::List:
c@68 378 VampnProto::buildRpcRequest_List(builder);
c@29 379 break;
c@29 380 case RRType::Load:
c@68 381 VampnProto::buildRpcRequest_Load(builder, rr.loadRequest);
c@29 382 break;
c@29 383 case RRType::Configure:
c@68 384 VampnProto::buildRpcRequest_Configure(builder,
c@68 385 rr.configurationRequest, mapper);
c@29 386 break;
c@29 387 case RRType::Process:
c@68 388 VampnProto::buildRpcRequest_Process(builder, rr.processRequest, mapper);
c@29 389 break;
c@29 390 case RRType::Finish:
c@68 391 VampnProto::buildRpcRequest_Finish(builder, rr.finishRequest, mapper);
c@29 392 break;
c@29 393 case RRType::NotValid:
c@29 394 break;
c@29 395 }
c@29 396
c@33 397 writeMessageToFd(1, message);
c@29 398 }
c@29 399
c@27 400 RequestOrResponse
c@33 401 readResponseCapnp(kj::BufferedInputStreamWrapper &buffered)
c@27 402 {
c@27 403 RequestOrResponse rr;
c@27 404 rr.direction = RequestOrResponse::Response;
c@27 405
c@33 406 ::capnp::InputStreamMessageReader message(buffered);
c@68 407 RpcResponse::Reader reader = message.getRoot<RpcResponse>();
c@27 408
c@27 409 rr.type = VampnProto::getRequestResponseType(reader);
c@68 410 rr.success = true;
c@68 411 rr.errorText = "";
c@73 412 rr.id = readCapnpId(reader);
c@68 413 int errorCode = 0;
c@27 414
c@27 415 switch (rr.type) {
c@27 416
c@27 417 case RRType::List:
c@68 418 VampnProto::readRpcResponse_List(rr.listResponse, reader);
c@27 419 break;
c@27 420 case RRType::Load:
c@68 421 VampnProto::readRpcResponse_Load(rr.loadResponse, reader, mapper);
c@27 422 break;
c@27 423 case RRType::Configure:
c@68 424 VampnProto::readRpcResponse_Configure(rr.configurationResponse,
c@55 425 reader, mapper);
c@27 426 break;
c@27 427 case RRType::Process:
c@68 428 VampnProto::readRpcResponse_Process(rr.processResponse, reader, mapper);
c@27 429 break;
c@27 430 case RRType::Finish:
c@68 431 VampnProto::readRpcResponse_Finish(rr.finishResponse, reader, mapper);
c@27 432 break;
c@27 433 case RRType::NotValid:
c@68 434 // error
c@68 435 rr.success = false;
c@68 436 VampnProto::readRpcResponse_Error(errorCode, rr.errorText, reader);
c@27 437 break;
c@27 438 }
c@27 439
c@27 440 return rr;
c@24 441 }
c@24 442
c@29 443 void
c@29 444 writeResponseCapnp(RequestOrResponse &rr)
c@29 445 {
c@29 446 ::capnp::MallocMessageBuilder message;
c@68 447 RpcResponse::Builder builder = message.initRoot<RpcResponse>();
c@29 448
c@73 449 buildCapnpId(builder, rr.id);
c@73 450
c@52 451 if (!rr.success) {
c@29 452
c@68 453 VampnProto::buildRpcResponse_Error(builder, rr.errorText, rr.type);
c@52 454
c@52 455 } else {
c@52 456
c@52 457 switch (rr.type) {
c@52 458
c@52 459 case RRType::List:
c@68 460 VampnProto::buildRpcResponse_List(builder, rr.listResponse);
c@52 461 break;
c@52 462 case RRType::Load:
c@68 463 VampnProto::buildRpcResponse_Load(builder, rr.loadResponse, mapper);
c@52 464 break;
c@52 465 case RRType::Configure:
c@68 466 VampnProto::buildRpcResponse_Configure(builder, rr.configurationResponse, mapper);
c@52 467 break;
c@52 468 case RRType::Process:
c@68 469 VampnProto::buildRpcResponse_Process(builder, rr.processResponse, mapper);
c@52 470 break;
c@52 471 case RRType::Finish:
c@68 472 VampnProto::buildRpcResponse_Finish(builder, rr.finishResponse, mapper);
c@52 473 break;
c@52 474 case RRType::NotValid:
c@52 475 break;
c@52 476 }
c@29 477 }
c@52 478
c@33 479 writeMessageToFd(1, message);
c@33 480 }
c@33 481
c@33 482 RequestOrResponse
c@60 483 readInputJson(RequestOrResponse::Direction direction, string &err)
c@33 484 {
c@33 485 if (direction == RequestOrResponse::Request) {
c@60 486 return readRequestJson(err);
c@33 487 } else {
c@60 488 return readResponseJson(err);
c@33 489 }
c@33 490 }
c@33 491
c@33 492 RequestOrResponse
c@33 493 readInputCapnp(RequestOrResponse::Direction direction)
c@33 494 {
c@33 495 static kj::FdInputStream stream(0); // stdin
c@33 496 static kj::BufferedInputStreamWrapper buffered(stream);
c@33 497
c@33 498 if (buffered.tryGetReadBuffer() == nullptr) {
c@33 499 return {};
c@33 500 }
c@33 501
c@33 502 if (direction == RequestOrResponse::Request) {
c@33 503 return readRequestCapnp(buffered);
c@33 504 } else {
c@33 505 return readResponseCapnp(buffered);
c@33 506 }
c@29 507 }
c@29 508
c@23 509 RequestOrResponse
c@24 510 readInput(string format, RequestOrResponse::Direction direction)
c@23 511 {
c@23 512 if (format == "json") {
c@60 513 string err;
c@60 514 auto result = readInputJson(direction, err);
c@60 515 if (err != "") throw runtime_error(err);
c@60 516 else return result;
c@27 517 } else if (format == "capnp") {
c@33 518 return readInputCapnp(direction);
c@23 519 } else {
c@27 520 throw runtime_error("unknown input format \"" + format + "\"");
c@23 521 }
c@23 522 }
c@23 523
c@23 524 void
c@24 525 writeOutput(string format, RequestOrResponse &rr)
c@23 526 {
c@24 527 if (format == "json") {
c@24 528 if (rr.direction == RequestOrResponse::Request) {
c@44 529 writeRequestJson(rr, false);
c@24 530 } else {
c@44 531 writeResponseJson(rr, false);
c@44 532 }
c@44 533 } else if (format == "json-b64") {
c@44 534 if (rr.direction == RequestOrResponse::Request) {
c@44 535 writeRequestJson(rr, true);
c@44 536 } else {
c@44 537 writeResponseJson(rr, true);
c@24 538 }
c@29 539 } else if (format == "capnp") {
c@29 540 if (rr.direction == RequestOrResponse::Request) {
c@29 541 writeRequestCapnp(rr);
c@29 542 } else {
c@29 543 writeResponseCapnp(rr);
c@29 544 }
c@24 545 } else {
c@27 546 throw runtime_error("unknown output format \"" + format + "\"");
c@24 547 }
c@23 548 }
c@23 549
c@23 550 int main(int argc, char **argv)
c@23 551 {
c@24 552 if (argc < 2) {
c@23 553 usage();
c@23 554 }
c@23 555
c@24 556 string informat = "json", outformat = "json";
c@27 557 RequestOrResponse::Direction direction = RequestOrResponse::Request;
c@24 558 bool haveDirection = false;
c@23 559
c@24 560 for (int i = 1; i < argc; ++i) {
c@23 561
c@23 562 string arg = argv[i];
c@24 563 bool final = (i + 1 == argc);
c@23 564
c@23 565 if (arg == "-i") {
c@27 566 if (final) usage();
c@23 567 else informat = argv[++i];
c@23 568
c@23 569 } else if (arg == "-o") {
c@27 570 if (final) usage();
c@23 571 else outformat = argv[++i];
c@23 572
c@24 573 } else if (arg == "request") {
c@24 574 direction = RequestOrResponse::Request;
c@24 575 haveDirection = true;
c@24 576
c@24 577 } else if (arg == "response") {
c@24 578 direction = RequestOrResponse::Response;
c@24 579 haveDirection = true;
c@24 580
c@23 581 } else {
c@23 582 usage();
c@23 583 }
c@23 584 }
c@23 585
c@24 586 if (informat == "" || outformat == "" || !haveDirection) {
c@23 587 usage();
c@23 588 }
c@23 589
c@23 590 while (true) {
c@23 591
c@23 592 try {
c@23 593
c@24 594 RequestOrResponse rr = readInput(informat, direction);
c@29 595
c@29 596 // NotValid without an exception indicates EOF:
c@25 597 if (rr.type == RRType::NotValid) break;
c@29 598
c@23 599 writeOutput(outformat, rr);
c@23 600
c@23 601 } catch (std::exception &e) {
c@52 602
c@23 603 cerr << "Error: " << e.what() << endl;
c@23 604 exit(1);
c@23 605 }
c@23 606 }
c@23 607
c@23 608 exit(0);
c@23 609 }
c@23 610
c@23 611