annotate utilities/vampipe-convert.cpp @ 60:8a4bcb3dc3a6

Replace exceptions throughout the JSON-handling and adapter code with string-arg error handling. No longer need exception handling enabled in Emscripten (with its consequent runtime overhead - though we still need to check whether this error handling regime is actually faster).
author Chris Cannam <c.cannam@qmul.ac.uk>
date Tue, 20 Sep 2016 16:35:47 +0100
parents 77833938f0f8
children 6f160dee1192
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@60 49 } else if (!j["type"].is_string()) {
c@60 50 err = "string expected for type field";
c@60 51 } else if (!j["content"].is_null() && !j["content"].is_object()) {
c@60 52 err = "object expected for content 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@60 67 } else if (!j["success"].is_bool()) {
c@60 68 err = "bool expected for success field";
c@60 69 } else if (!j["content"].is_object()) {
c@60 70 err = "object expected for content field";
c@24 71 }
c@24 72 return j;
c@24 73 }
c@24 74
c@30 75 //!!! Lots of potential for refactoring the conversion classes based
c@30 76 //!!! on the common matter in the following eight functions...
c@30 77
c@32 78 PreservingPluginHandleMapper mapper;
c@32 79
c@23 80 RequestOrResponse
c@60 81 readRequestJson(string &err)
c@23 82 {
c@23 83 RequestOrResponse rr;
c@24 84 rr.direction = RequestOrResponse::Request;
c@24 85
c@24 86 string input;
c@24 87 if (!getline(cin, input)) {
c@60 88 // the EOF case, not actually an error
c@25 89 rr.type = RRType::NotValid;
c@24 90 return rr;
c@24 91 }
c@24 92
c@60 93 Json j = convertRequestJson(input, err);
c@60 94 if (err != "") return {};
c@27 95
c@60 96 rr.type = VampJson::getRequestResponseType(j, err);
c@60 97 if (err != "") return {};
c@60 98
c@44 99 VampJson::BufferSerialisation serialisation = VampJson::BufferSerialisation::Text;
c@24 100
c@27 101 switch (rr.type) {
c@27 102
c@27 103 case RRType::List:
c@60 104 VampJson::toVampRequest_List(j, err); // type check only
c@27 105 break;
c@27 106 case RRType::Load:
c@60 107 rr.loadRequest = VampJson::toVampRequest_Load(j, err);
c@27 108 break;
c@27 109 case RRType::Configure:
c@60 110 rr.configurationRequest = VampJson::toVampRequest_Configure(j, mapper, err);
c@27 111 break;
c@27 112 case RRType::Process:
c@60 113 rr.processRequest = VampJson::toVampRequest_Process(j, mapper, serialisation, err);
c@27 114 break;
c@27 115 case RRType::Finish:
c@60 116 rr.finishRequest = VampJson::toVampRequest_Finish(j, mapper, err);
c@27 117 break;
c@27 118 case RRType::NotValid:
c@27 119 break;
c@24 120 }
c@24 121
c@24 122 return rr;
c@24 123 }
c@24 124
c@24 125 void
c@44 126 writeRequestJson(RequestOrResponse &rr, bool useBase64)
c@24 127 {
c@24 128 Json j;
c@24 129
c@54 130 VampJson::BufferSerialisation serialisation =
c@54 131 (useBase64 ?
c@54 132 VampJson::BufferSerialisation::Base64 :
c@54 133 VampJson::BufferSerialisation::Text);
c@54 134
c@27 135 switch (rr.type) {
c@27 136
c@27 137 case RRType::List:
c@24 138 j = VampJson::fromVampRequest_List();
c@27 139 break;
c@27 140 case RRType::Load:
c@24 141 j = VampJson::fromVampRequest_Load(rr.loadRequest);
c@27 142 break;
c@27 143 case RRType::Configure:
c@32 144 j = VampJson::fromVampRequest_Configure(rr.configurationRequest, mapper);
c@27 145 break;
c@27 146 case RRType::Process:
c@44 147 j = VampJson::fromVampRequest_Process
c@54 148 (rr.processRequest, mapper, serialisation);
c@27 149 break;
c@27 150 case RRType::Finish:
c@55 151 j = VampJson::fromVampRequest_Finish(rr.finishRequest, mapper);
c@27 152 break;
c@27 153 case RRType::NotValid:
c@27 154 break;
c@24 155 }
c@24 156
c@24 157 cout << j.dump() << endl;
c@24 158 }
c@24 159
c@24 160 RequestOrResponse
c@60 161 readResponseJson(string &err)
c@24 162 {
c@24 163 RequestOrResponse rr;
c@24 164 rr.direction = RequestOrResponse::Response;
c@24 165
c@23 166 string input;
c@23 167 if (!getline(cin, input)) {
c@60 168 // the EOF case, not actually an error
c@25 169 rr.type = RRType::NotValid;
c@23 170 return rr;
c@23 171 }
c@23 172
c@60 173 Json j = convertResponseJson(input, err);
c@60 174 if (err != "") return {};
c@27 175
c@60 176 rr.type = VampJson::getRequestResponseType(j, err);
c@60 177 if (err != "") return {};
c@60 178
c@44 179 VampJson::BufferSerialisation serialisation = VampJson::BufferSerialisation::Text;
c@23 180
c@52 181 rr.success = j["success"].bool_value();
c@52 182 rr.errorText = j["errorText"].string_value();
c@52 183
c@27 184 switch (rr.type) {
c@27 185
c@27 186 case RRType::List:
c@60 187 rr.listResponse = VampJson::toVampResponse_List(j, err);
c@27 188 break;
c@27 189 case RRType::Load:
c@60 190 rr.loadResponse = VampJson::toVampResponse_Load(j, mapper, err);
c@27 191 break;
c@27 192 case RRType::Configure:
c@60 193 rr.configurationResponse = VampJson::toVampResponse_Configure(j, mapper, err);
c@27 194 break;
c@27 195 case RRType::Process:
c@60 196 rr.processResponse = VampJson::toVampResponse_Process(j, mapper, serialisation, err);
c@27 197 break;
c@27 198 case RRType::Finish:
c@60 199 rr.finishResponse = VampJson::toVampResponse_Finish(j, mapper, serialisation, err);
c@27 200 break;
c@27 201 case RRType::NotValid:
c@27 202 break;
c@23 203 }
c@23 204
c@23 205 return rr;
c@23 206 }
c@23 207
c@24 208 void
c@44 209 writeResponseJson(RequestOrResponse &rr, bool useBase64)
c@24 210 {
c@24 211 Json j;
c@24 212
c@53 213 VampJson::BufferSerialisation serialisation =
c@54 214 (useBase64 ?
c@53 215 VampJson::BufferSerialisation::Base64 :
c@53 216 VampJson::BufferSerialisation::Text);
c@53 217
c@52 218 if (!rr.success) {
c@27 219
c@52 220 j = VampJson::fromError(rr.errorText, rr.type);
c@52 221
c@52 222 } else {
c@52 223
c@52 224 switch (rr.type) {
c@52 225
c@52 226 case RRType::List:
c@59 227 j = VampJson::fromVampResponse_List(rr.listResponse);
c@52 228 break;
c@52 229 case RRType::Load:
c@52 230 j = VampJson::fromVampResponse_Load(rr.loadResponse, mapper);
c@52 231 break;
c@52 232 case RRType::Configure:
c@55 233 j = VampJson::fromVampResponse_Configure(rr.configurationResponse,
c@55 234 mapper);
c@52 235 break;
c@52 236 case RRType::Process:
c@52 237 j = VampJson::fromVampResponse_Process
c@53 238 (rr.processResponse, mapper, serialisation);
c@52 239 break;
c@52 240 case RRType::Finish:
c@52 241 j = VampJson::fromVampResponse_Finish
c@53 242 (rr.finishResponse, mapper, serialisation);
c@52 243 break;
c@52 244 case RRType::NotValid:
c@52 245 break;
c@52 246 }
c@24 247 }
c@52 248
c@27 249 cout << j.dump() << endl;
c@27 250 }
c@25 251
c@27 252 RequestOrResponse
c@33 253 readRequestCapnp(kj::BufferedInputStreamWrapper &buffered)
c@27 254 {
c@27 255 RequestOrResponse rr;
c@27 256 rr.direction = RequestOrResponse::Request;
c@27 257
c@33 258 ::capnp::InputStreamMessageReader message(buffered);
c@27 259 VampRequest::Reader reader = message.getRoot<VampRequest>();
c@27 260
c@27 261 rr.type = VampnProto::getRequestResponseType(reader);
c@27 262
c@27 263 switch (rr.type) {
c@27 264
c@27 265 case RRType::List:
c@27 266 VampnProto::readVampRequest_List(reader); // type check only
c@27 267 break;
c@27 268 case RRType::Load:
c@27 269 VampnProto::readVampRequest_Load(rr.loadRequest, reader);
c@27 270 break;
c@27 271 case RRType::Configure:
c@32 272 VampnProto::readVampRequest_Configure(rr.configurationRequest,
c@32 273 reader, mapper);
c@27 274 break;
c@27 275 case RRType::Process:
c@32 276 VampnProto::readVampRequest_Process(rr.processRequest, reader, mapper);
c@27 277 break;
c@27 278 case RRType::Finish:
c@55 279 VampnProto::readVampRequest_Finish(rr.finishRequest, reader, mapper);
c@27 280 break;
c@27 281 case RRType::NotValid:
c@27 282 break;
c@27 283 }
c@27 284
c@27 285 return rr;
c@27 286 }
c@27 287
c@29 288 void
c@29 289 writeRequestCapnp(RequestOrResponse &rr)
c@29 290 {
c@29 291 ::capnp::MallocMessageBuilder message;
c@29 292 VampRequest::Builder builder = message.initRoot<VampRequest>();
c@29 293
c@29 294 switch (rr.type) {
c@29 295
c@29 296 case RRType::List:
c@29 297 VampnProto::buildVampRequest_List(builder);
c@29 298 break;
c@29 299 case RRType::Load:
c@29 300 VampnProto::buildVampRequest_Load(builder, rr.loadRequest);
c@29 301 break;
c@29 302 case RRType::Configure:
c@29 303 VampnProto::buildVampRequest_Configure(builder,
c@32 304 rr.configurationRequest, mapper);
c@29 305 break;
c@29 306 case RRType::Process:
c@32 307 VampnProto::buildVampRequest_Process(builder, rr.processRequest, mapper);
c@29 308 break;
c@29 309 case RRType::Finish:
c@55 310 VampnProto::buildVampRequest_Finish(builder, rr.finishRequest, mapper);
c@29 311 break;
c@29 312 case RRType::NotValid:
c@29 313 break;
c@29 314 }
c@29 315
c@33 316 writeMessageToFd(1, message);
c@29 317 }
c@29 318
c@27 319 RequestOrResponse
c@33 320 readResponseCapnp(kj::BufferedInputStreamWrapper &buffered)
c@27 321 {
c@27 322 RequestOrResponse rr;
c@27 323 rr.direction = RequestOrResponse::Response;
c@27 324
c@33 325 ::capnp::InputStreamMessageReader message(buffered);
c@27 326 VampResponse::Reader reader = message.getRoot<VampResponse>();
c@27 327
c@27 328 rr.type = VampnProto::getRequestResponseType(reader);
c@52 329 rr.success = reader.getSuccess();
c@52 330 rr.errorText = reader.getErrorText();
c@27 331
c@27 332 switch (rr.type) {
c@27 333
c@27 334 case RRType::List:
c@27 335 VampnProto::readVampResponse_List(rr.listResponse, reader);
c@27 336 break;
c@27 337 case RRType::Load:
c@32 338 VampnProto::readVampResponse_Load(rr.loadResponse, reader, mapper);
c@27 339 break;
c@27 340 case RRType::Configure:
c@27 341 VampnProto::readVampResponse_Configure(rr.configurationResponse,
c@55 342 reader, mapper);
c@27 343 break;
c@27 344 case RRType::Process:
c@51 345 VampnProto::readVampResponse_Process(rr.processResponse, reader, mapper);
c@27 346 break;
c@27 347 case RRType::Finish:
c@51 348 VampnProto::readVampResponse_Finish(rr.finishResponse, reader, mapper);
c@27 349 break;
c@27 350 case RRType::NotValid:
c@27 351 break;
c@27 352 }
c@27 353
c@27 354 return rr;
c@24 355 }
c@24 356
c@29 357 void
c@29 358 writeResponseCapnp(RequestOrResponse &rr)
c@29 359 {
c@29 360 ::capnp::MallocMessageBuilder message;
c@29 361 VampResponse::Builder builder = message.initRoot<VampResponse>();
c@29 362
c@52 363 if (!rr.success) {
c@29 364
c@52 365 VampnProto::buildVampResponse_Error(builder, rr.errorText, rr.type);
c@52 366
c@52 367 } else {
c@52 368
c@52 369 switch (rr.type) {
c@52 370
c@52 371 case RRType::List:
c@56 372 VampnProto::buildVampResponse_List(builder, rr.listResponse);
c@52 373 break;
c@52 374 case RRType::Load:
c@52 375 VampnProto::buildVampResponse_Load(builder, rr.loadResponse, mapper);
c@52 376 break;
c@52 377 case RRType::Configure:
c@55 378 VampnProto::buildVampResponse_Configure(builder, rr.configurationResponse, mapper);
c@52 379 break;
c@52 380 case RRType::Process:
c@52 381 VampnProto::buildVampResponse_Process(builder, rr.processResponse, mapper);
c@52 382 break;
c@52 383 case RRType::Finish:
c@52 384 VampnProto::buildVampResponse_Finish(builder, rr.finishResponse, mapper);
c@52 385 break;
c@52 386 case RRType::NotValid:
c@52 387 break;
c@52 388 }
c@29 389 }
c@52 390
c@33 391 writeMessageToFd(1, message);
c@33 392 }
c@33 393
c@33 394 RequestOrResponse
c@60 395 readInputJson(RequestOrResponse::Direction direction, string &err)
c@33 396 {
c@33 397 if (direction == RequestOrResponse::Request) {
c@60 398 return readRequestJson(err);
c@33 399 } else {
c@60 400 return readResponseJson(err);
c@33 401 }
c@33 402 }
c@33 403
c@33 404 RequestOrResponse
c@33 405 readInputCapnp(RequestOrResponse::Direction direction)
c@33 406 {
c@33 407 static kj::FdInputStream stream(0); // stdin
c@33 408 static kj::BufferedInputStreamWrapper buffered(stream);
c@33 409
c@33 410 if (buffered.tryGetReadBuffer() == nullptr) {
c@33 411 return {};
c@33 412 }
c@33 413
c@33 414 if (direction == RequestOrResponse::Request) {
c@33 415 return readRequestCapnp(buffered);
c@33 416 } else {
c@33 417 return readResponseCapnp(buffered);
c@33 418 }
c@29 419 }
c@29 420
c@23 421 RequestOrResponse
c@24 422 readInput(string format, RequestOrResponse::Direction direction)
c@23 423 {
c@23 424 if (format == "json") {
c@60 425 string err;
c@60 426 auto result = readInputJson(direction, err);
c@60 427 if (err != "") throw runtime_error(err);
c@60 428 else return result;
c@27 429 } else if (format == "capnp") {
c@33 430 return readInputCapnp(direction);
c@23 431 } else {
c@27 432 throw runtime_error("unknown input format \"" + format + "\"");
c@23 433 }
c@23 434 }
c@23 435
c@23 436 void
c@24 437 writeOutput(string format, RequestOrResponse &rr)
c@23 438 {
c@24 439 if (format == "json") {
c@24 440 if (rr.direction == RequestOrResponse::Request) {
c@44 441 writeRequestJson(rr, false);
c@24 442 } else {
c@44 443 writeResponseJson(rr, false);
c@44 444 }
c@44 445 } else if (format == "json-b64") {
c@44 446 if (rr.direction == RequestOrResponse::Request) {
c@44 447 writeRequestJson(rr, true);
c@44 448 } else {
c@44 449 writeResponseJson(rr, true);
c@24 450 }
c@29 451 } else if (format == "capnp") {
c@29 452 if (rr.direction == RequestOrResponse::Request) {
c@29 453 writeRequestCapnp(rr);
c@29 454 } else {
c@29 455 writeResponseCapnp(rr);
c@29 456 }
c@24 457 } else {
c@27 458 throw runtime_error("unknown output format \"" + format + "\"");
c@24 459 }
c@23 460 }
c@23 461
c@23 462 int main(int argc, char **argv)
c@23 463 {
c@24 464 if (argc < 2) {
c@23 465 usage();
c@23 466 }
c@23 467
c@24 468 string informat = "json", outformat = "json";
c@27 469 RequestOrResponse::Direction direction = RequestOrResponse::Request;
c@24 470 bool haveDirection = false;
c@23 471
c@24 472 for (int i = 1; i < argc; ++i) {
c@23 473
c@23 474 string arg = argv[i];
c@24 475 bool final = (i + 1 == argc);
c@23 476
c@23 477 if (arg == "-i") {
c@27 478 if (final) usage();
c@23 479 else informat = argv[++i];
c@23 480
c@23 481 } else if (arg == "-o") {
c@27 482 if (final) usage();
c@23 483 else outformat = argv[++i];
c@23 484
c@24 485 } else if (arg == "request") {
c@24 486 direction = RequestOrResponse::Request;
c@24 487 haveDirection = true;
c@24 488
c@24 489 } else if (arg == "response") {
c@24 490 direction = RequestOrResponse::Response;
c@24 491 haveDirection = true;
c@24 492
c@23 493 } else {
c@23 494 usage();
c@23 495 }
c@23 496 }
c@23 497
c@24 498 if (informat == "" || outformat == "" || !haveDirection) {
c@23 499 usage();
c@23 500 }
c@23 501
c@23 502 while (true) {
c@23 503
c@23 504 try {
c@23 505
c@24 506 RequestOrResponse rr = readInput(informat, direction);
c@29 507
c@29 508 // NotValid without an exception indicates EOF:
c@25 509 if (rr.type == RRType::NotValid) break;
c@29 510
c@23 511 writeOutput(outformat, rr);
c@23 512
c@23 513 } catch (std::exception &e) {
c@52 514
c@23 515 cerr << "Error: " << e.what() << endl;
c@23 516 exit(1);
c@23 517 }
c@23 518 }
c@23 519
c@23 520 exit(0);
c@23 521 }
c@23 522
c@23 523