annotate utilities/vampipe-convert.cpp @ 71:6c908ee3ad3b

vamp -> piper for IDL specs
author Chris Cannam <c.cannam@qmul.ac.uk>
date Fri, 07 Oct 2016 14:27:11 +0100
parents a5ba837bca28
children 7bfc07576830
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@23 82 RequestOrResponse
c@60 83 readRequestJson(string &err)
c@23 84 {
c@23 85 RequestOrResponse rr;
c@24 86 rr.direction = RequestOrResponse::Request;
c@24 87
c@24 88 string input;
c@24 89 if (!getline(cin, input)) {
c@60 90 // the EOF case, not actually an error
c@25 91 rr.type = RRType::NotValid;
c@24 92 return rr;
c@24 93 }
c@24 94
c@60 95 Json j = convertRequestJson(input, err);
c@60 96 if (err != "") return {};
c@27 97
c@60 98 rr.type = VampJson::getRequestResponseType(j, err);
c@60 99 if (err != "") return {};
c@60 100
c@66 101 VampJson::BufferSerialisation serialisation =
c@66 102 VampJson::BufferSerialisation::Array;
c@24 103
c@27 104 switch (rr.type) {
c@27 105
c@27 106 case RRType::List:
c@68 107 VampJson::toRpcRequest_List(j, err); // type check only
c@27 108 break;
c@27 109 case RRType::Load:
c@68 110 rr.loadRequest = VampJson::toRpcRequest_Load(j, err);
c@27 111 break;
c@27 112 case RRType::Configure:
c@68 113 rr.configurationRequest = VampJson::toRpcRequest_Configure(j, mapper, err);
c@27 114 break;
c@27 115 case RRType::Process:
c@68 116 rr.processRequest = VampJson::toRpcRequest_Process(j, mapper, serialisation, err);
c@27 117 break;
c@27 118 case RRType::Finish:
c@68 119 rr.finishRequest = VampJson::toRpcRequest_Finish(j, mapper, err);
c@27 120 break;
c@27 121 case RRType::NotValid:
c@27 122 break;
c@24 123 }
c@24 124
c@24 125 return rr;
c@24 126 }
c@24 127
c@24 128 void
c@44 129 writeRequestJson(RequestOrResponse &rr, bool useBase64)
c@24 130 {
c@24 131 Json j;
c@24 132
c@54 133 VampJson::BufferSerialisation serialisation =
c@54 134 (useBase64 ?
c@54 135 VampJson::BufferSerialisation::Base64 :
c@66 136 VampJson::BufferSerialisation::Array);
c@54 137
c@27 138 switch (rr.type) {
c@27 139
c@27 140 case RRType::List:
c@68 141 j = VampJson::fromRpcRequest_List();
c@27 142 break;
c@27 143 case RRType::Load:
c@68 144 j = VampJson::fromRpcRequest_Load(rr.loadRequest);
c@27 145 break;
c@27 146 case RRType::Configure:
c@68 147 j = VampJson::fromRpcRequest_Configure(rr.configurationRequest, mapper);
c@27 148 break;
c@27 149 case RRType::Process:
c@68 150 j = VampJson::fromRpcRequest_Process
c@54 151 (rr.processRequest, mapper, serialisation);
c@27 152 break;
c@27 153 case RRType::Finish:
c@68 154 j = VampJson::fromRpcRequest_Finish(rr.finishRequest, mapper);
c@27 155 break;
c@27 156 case RRType::NotValid:
c@27 157 break;
c@24 158 }
c@24 159
c@24 160 cout << j.dump() << endl;
c@24 161 }
c@24 162
c@24 163 RequestOrResponse
c@60 164 readResponseJson(string &err)
c@24 165 {
c@24 166 RequestOrResponse rr;
c@24 167 rr.direction = RequestOrResponse::Response;
c@24 168
c@23 169 string input;
c@23 170 if (!getline(cin, input)) {
c@60 171 // the EOF case, not actually an error
c@25 172 rr.type = RRType::NotValid;
c@23 173 return rr;
c@23 174 }
c@23 175
c@60 176 Json j = convertResponseJson(input, err);
c@60 177 if (err != "") return {};
c@27 178
c@60 179 rr.type = VampJson::getRequestResponseType(j, err);
c@60 180 if (err != "") return {};
c@60 181
c@66 182 VampJson::BufferSerialisation serialisation =
c@66 183 VampJson::BufferSerialisation::Array;
c@23 184
c@52 185 rr.success = j["success"].bool_value();
c@52 186 rr.errorText = j["errorText"].string_value();
c@52 187
c@27 188 switch (rr.type) {
c@27 189
c@27 190 case RRType::List:
c@68 191 rr.listResponse = VampJson::toRpcResponse_List(j, err);
c@27 192 break;
c@27 193 case RRType::Load:
c@68 194 rr.loadResponse = VampJson::toRpcResponse_Load(j, mapper, err);
c@27 195 break;
c@27 196 case RRType::Configure:
c@68 197 rr.configurationResponse = VampJson::toRpcResponse_Configure(j, mapper, err);
c@27 198 break;
c@27 199 case RRType::Process:
c@68 200 rr.processResponse = VampJson::toRpcResponse_Process(j, mapper, serialisation, err);
c@27 201 break;
c@27 202 case RRType::Finish:
c@68 203 rr.finishResponse = VampJson::toRpcResponse_Finish(j, mapper, serialisation, err);
c@27 204 break;
c@27 205 case RRType::NotValid:
c@27 206 break;
c@23 207 }
c@23 208
c@23 209 return rr;
c@23 210 }
c@23 211
c@24 212 void
c@44 213 writeResponseJson(RequestOrResponse &rr, bool useBase64)
c@24 214 {
c@24 215 Json j;
c@24 216
c@53 217 VampJson::BufferSerialisation serialisation =
c@54 218 (useBase64 ?
c@53 219 VampJson::BufferSerialisation::Base64 :
c@66 220 VampJson::BufferSerialisation::Array);
c@53 221
c@52 222 if (!rr.success) {
c@27 223
c@52 224 j = VampJson::fromError(rr.errorText, rr.type);
c@52 225
c@52 226 } else {
c@52 227
c@52 228 switch (rr.type) {
c@52 229
c@52 230 case RRType::List:
c@68 231 j = VampJson::fromRpcResponse_List(rr.listResponse);
c@52 232 break;
c@52 233 case RRType::Load:
c@68 234 j = VampJson::fromRpcResponse_Load(rr.loadResponse, mapper);
c@52 235 break;
c@52 236 case RRType::Configure:
c@68 237 j = VampJson::fromRpcResponse_Configure(rr.configurationResponse,
c@55 238 mapper);
c@52 239 break;
c@52 240 case RRType::Process:
c@68 241 j = VampJson::fromRpcResponse_Process
c@53 242 (rr.processResponse, mapper, serialisation);
c@52 243 break;
c@52 244 case RRType::Finish:
c@68 245 j = VampJson::fromRpcResponse_Finish
c@53 246 (rr.finishResponse, mapper, serialisation);
c@52 247 break;
c@52 248 case RRType::NotValid:
c@52 249 break;
c@52 250 }
c@24 251 }
c@52 252
c@27 253 cout << j.dump() << endl;
c@27 254 }
c@25 255
c@27 256 RequestOrResponse
c@33 257 readRequestCapnp(kj::BufferedInputStreamWrapper &buffered)
c@27 258 {
c@27 259 RequestOrResponse rr;
c@27 260 rr.direction = RequestOrResponse::Request;
c@27 261
c@33 262 ::capnp::InputStreamMessageReader message(buffered);
c@68 263 RpcRequest::Reader reader = message.getRoot<RpcRequest>();
c@27 264
c@27 265 rr.type = VampnProto::getRequestResponseType(reader);
c@27 266
c@27 267 switch (rr.type) {
c@27 268
c@27 269 case RRType::List:
c@68 270 VampnProto::readRpcRequest_List(reader); // type check only
c@27 271 break;
c@27 272 case RRType::Load:
c@68 273 VampnProto::readRpcRequest_Load(rr.loadRequest, reader);
c@27 274 break;
c@27 275 case RRType::Configure:
c@68 276 VampnProto::readRpcRequest_Configure(rr.configurationRequest,
c@32 277 reader, mapper);
c@27 278 break;
c@27 279 case RRType::Process:
c@68 280 VampnProto::readRpcRequest_Process(rr.processRequest, reader, mapper);
c@27 281 break;
c@27 282 case RRType::Finish:
c@68 283 VampnProto::readRpcRequest_Finish(rr.finishRequest, reader, mapper);
c@27 284 break;
c@27 285 case RRType::NotValid:
c@27 286 break;
c@27 287 }
c@27 288
c@27 289 return rr;
c@27 290 }
c@27 291
c@29 292 void
c@29 293 writeRequestCapnp(RequestOrResponse &rr)
c@29 294 {
c@29 295 ::capnp::MallocMessageBuilder message;
c@68 296 RpcRequest::Builder builder = message.initRoot<RpcRequest>();
c@29 297
c@29 298 switch (rr.type) {
c@29 299
c@29 300 case RRType::List:
c@68 301 VampnProto::buildRpcRequest_List(builder);
c@29 302 break;
c@29 303 case RRType::Load:
c@68 304 VampnProto::buildRpcRequest_Load(builder, rr.loadRequest);
c@29 305 break;
c@29 306 case RRType::Configure:
c@68 307 VampnProto::buildRpcRequest_Configure(builder,
c@68 308 rr.configurationRequest, mapper);
c@29 309 break;
c@29 310 case RRType::Process:
c@68 311 VampnProto::buildRpcRequest_Process(builder, rr.processRequest, mapper);
c@29 312 break;
c@29 313 case RRType::Finish:
c@68 314 VampnProto::buildRpcRequest_Finish(builder, rr.finishRequest, mapper);
c@29 315 break;
c@29 316 case RRType::NotValid:
c@29 317 break;
c@29 318 }
c@29 319
c@33 320 writeMessageToFd(1, message);
c@29 321 }
c@29 322
c@27 323 RequestOrResponse
c@33 324 readResponseCapnp(kj::BufferedInputStreamWrapper &buffered)
c@27 325 {
c@27 326 RequestOrResponse rr;
c@27 327 rr.direction = RequestOrResponse::Response;
c@27 328
c@33 329 ::capnp::InputStreamMessageReader message(buffered);
c@68 330 RpcResponse::Reader reader = message.getRoot<RpcResponse>();
c@27 331
c@27 332 rr.type = VampnProto::getRequestResponseType(reader);
c@68 333 rr.success = true;
c@68 334 rr.errorText = "";
c@68 335 int errorCode = 0;
c@27 336
c@27 337 switch (rr.type) {
c@27 338
c@27 339 case RRType::List:
c@68 340 VampnProto::readRpcResponse_List(rr.listResponse, reader);
c@27 341 break;
c@27 342 case RRType::Load:
c@68 343 VampnProto::readRpcResponse_Load(rr.loadResponse, reader, mapper);
c@27 344 break;
c@27 345 case RRType::Configure:
c@68 346 VampnProto::readRpcResponse_Configure(rr.configurationResponse,
c@55 347 reader, mapper);
c@27 348 break;
c@27 349 case RRType::Process:
c@68 350 VampnProto::readRpcResponse_Process(rr.processResponse, reader, mapper);
c@27 351 break;
c@27 352 case RRType::Finish:
c@68 353 VampnProto::readRpcResponse_Finish(rr.finishResponse, reader, mapper);
c@27 354 break;
c@27 355 case RRType::NotValid:
c@68 356 // error
c@68 357 rr.success = false;
c@68 358 VampnProto::readRpcResponse_Error(errorCode, rr.errorText, reader);
c@27 359 break;
c@27 360 }
c@27 361
c@27 362 return rr;
c@24 363 }
c@24 364
c@29 365 void
c@29 366 writeResponseCapnp(RequestOrResponse &rr)
c@29 367 {
c@29 368 ::capnp::MallocMessageBuilder message;
c@68 369 RpcResponse::Builder builder = message.initRoot<RpcResponse>();
c@29 370
c@52 371 if (!rr.success) {
c@29 372
c@68 373 VampnProto::buildRpcResponse_Error(builder, rr.errorText, rr.type);
c@52 374
c@52 375 } else {
c@52 376
c@52 377 switch (rr.type) {
c@52 378
c@52 379 case RRType::List:
c@68 380 VampnProto::buildRpcResponse_List(builder, rr.listResponse);
c@52 381 break;
c@52 382 case RRType::Load:
c@68 383 VampnProto::buildRpcResponse_Load(builder, rr.loadResponse, mapper);
c@52 384 break;
c@52 385 case RRType::Configure:
c@68 386 VampnProto::buildRpcResponse_Configure(builder, rr.configurationResponse, mapper);
c@52 387 break;
c@52 388 case RRType::Process:
c@68 389 VampnProto::buildRpcResponse_Process(builder, rr.processResponse, mapper);
c@52 390 break;
c@52 391 case RRType::Finish:
c@68 392 VampnProto::buildRpcResponse_Finish(builder, rr.finishResponse, mapper);
c@52 393 break;
c@52 394 case RRType::NotValid:
c@52 395 break;
c@52 396 }
c@29 397 }
c@52 398
c@33 399 writeMessageToFd(1, message);
c@33 400 }
c@33 401
c@33 402 RequestOrResponse
c@60 403 readInputJson(RequestOrResponse::Direction direction, string &err)
c@33 404 {
c@33 405 if (direction == RequestOrResponse::Request) {
c@60 406 return readRequestJson(err);
c@33 407 } else {
c@60 408 return readResponseJson(err);
c@33 409 }
c@33 410 }
c@33 411
c@33 412 RequestOrResponse
c@33 413 readInputCapnp(RequestOrResponse::Direction direction)
c@33 414 {
c@33 415 static kj::FdInputStream stream(0); // stdin
c@33 416 static kj::BufferedInputStreamWrapper buffered(stream);
c@33 417
c@33 418 if (buffered.tryGetReadBuffer() == nullptr) {
c@33 419 return {};
c@33 420 }
c@33 421
c@33 422 if (direction == RequestOrResponse::Request) {
c@33 423 return readRequestCapnp(buffered);
c@33 424 } else {
c@33 425 return readResponseCapnp(buffered);
c@33 426 }
c@29 427 }
c@29 428
c@23 429 RequestOrResponse
c@24 430 readInput(string format, RequestOrResponse::Direction direction)
c@23 431 {
c@23 432 if (format == "json") {
c@60 433 string err;
c@60 434 auto result = readInputJson(direction, err);
c@60 435 if (err != "") throw runtime_error(err);
c@60 436 else return result;
c@27 437 } else if (format == "capnp") {
c@33 438 return readInputCapnp(direction);
c@23 439 } else {
c@27 440 throw runtime_error("unknown input format \"" + format + "\"");
c@23 441 }
c@23 442 }
c@23 443
c@23 444 void
c@24 445 writeOutput(string format, RequestOrResponse &rr)
c@23 446 {
c@24 447 if (format == "json") {
c@24 448 if (rr.direction == RequestOrResponse::Request) {
c@44 449 writeRequestJson(rr, false);
c@24 450 } else {
c@44 451 writeResponseJson(rr, false);
c@44 452 }
c@44 453 } else if (format == "json-b64") {
c@44 454 if (rr.direction == RequestOrResponse::Request) {
c@44 455 writeRequestJson(rr, true);
c@44 456 } else {
c@44 457 writeResponseJson(rr, true);
c@24 458 }
c@29 459 } else if (format == "capnp") {
c@29 460 if (rr.direction == RequestOrResponse::Request) {
c@29 461 writeRequestCapnp(rr);
c@29 462 } else {
c@29 463 writeResponseCapnp(rr);
c@29 464 }
c@24 465 } else {
c@27 466 throw runtime_error("unknown output format \"" + format + "\"");
c@24 467 }
c@23 468 }
c@23 469
c@23 470 int main(int argc, char **argv)
c@23 471 {
c@24 472 if (argc < 2) {
c@23 473 usage();
c@23 474 }
c@23 475
c@24 476 string informat = "json", outformat = "json";
c@27 477 RequestOrResponse::Direction direction = RequestOrResponse::Request;
c@24 478 bool haveDirection = false;
c@23 479
c@24 480 for (int i = 1; i < argc; ++i) {
c@23 481
c@23 482 string arg = argv[i];
c@24 483 bool final = (i + 1 == argc);
c@23 484
c@23 485 if (arg == "-i") {
c@27 486 if (final) usage();
c@23 487 else informat = argv[++i];
c@23 488
c@23 489 } else if (arg == "-o") {
c@27 490 if (final) usage();
c@23 491 else outformat = argv[++i];
c@23 492
c@24 493 } else if (arg == "request") {
c@24 494 direction = RequestOrResponse::Request;
c@24 495 haveDirection = true;
c@24 496
c@24 497 } else if (arg == "response") {
c@24 498 direction = RequestOrResponse::Response;
c@24 499 haveDirection = true;
c@24 500
c@23 501 } else {
c@23 502 usage();
c@23 503 }
c@23 504 }
c@23 505
c@24 506 if (informat == "" || outformat == "" || !haveDirection) {
c@23 507 usage();
c@23 508 }
c@23 509
c@23 510 while (true) {
c@23 511
c@23 512 try {
c@23 513
c@24 514 RequestOrResponse rr = readInput(informat, direction);
c@29 515
c@29 516 // NotValid without an exception indicates EOF:
c@25 517 if (rr.type == RRType::NotValid) break;
c@29 518
c@23 519 writeOutput(outformat, rr);
c@23 520
c@23 521 } catch (std::exception &e) {
c@52 522
c@23 523 cerr << "Error: " << e.what() << endl;
c@23 524 exit(1);
c@23 525 }
c@23 526 }
c@23 527
c@23 528 exit(0);
c@23 529 }
c@23 530
c@23 531